Shiro安全框架作用
- 认证,授权,加密,企业回话管理,缓存管理
- 权限管理模块开发
Shiro 和 Spring Security区别
- shiro 简单灵活,可脱离spring 粒度较粗
- Spring security 复杂 笨重 不能脱离spring 粒度较细
Shiro整体架构
Shiro认证
- 创建 SecurityManager 主体提交认证----》 SecurityManager认证
—》》Authenticator认证—》Realm验证
Shiro授权
- 创建SecurityManger ------>主体授权 ------------->SecurityManager授权
------>Authorizer授权------------->realm获取角色权限数据
ShiroRealm方式
IniRealm
package com.tmf.test;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Test;
public class IniRealmTest {
@Test
public void testAuthencation() {
IniRealm iniRealm = new IniRealm("classpath:user.ini");
//1.构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(iniRealm);
//2.主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("mark", "123456");
subject.login(token);
//是否认证
System.out.println("isAuthenticated:"+subject.isAuthenticated());
//用户角色
subject.checkRole("admin");
//用户权限
subject.checkPermission("user:update");
}
}
JdbcRealm
package com.tmf.test;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.subject.Subject;
import org.junit.Test;
import com.alibaba.druid.pool.DruidDataSource;
public class JdbcRealmTest {
//创建数据源
DruidDataSource dataSource = new DruidDataSource();
{
dataSource.setUrl("jdbc:mysql://localhost:3306/test");
dataSource.setUsername("root");
dataSource.setPassword("tiger");
}
@Test
public void testAuthencation() {
//Jdbc中有自己的默认查询语句
JdbcRealm jdbcRealm = new JdbcRealm();
jdbcRealm.setDataSource(dataSource);
//开启权限开关 默认为false不开启权限
jdbcRealm.setPermissionsLookupEnabled(true);
//设置自己的query
//密码查询
String sql = "select password from test_user where username=?";
jdbcRealm.setAuthenticationQuery(sql);
//角色查询
String sql1 = "select rolename from test_user_role where username=?";
jdbcRealm.setUserRolesQuery(sql1);
//权限查询
String sql2 ="select permission from test_role_permission where rolename=?";
jdbcRealm.setPermissionsQuery(sql2);
//1.构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(jdbcRealm);
//2.主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("xiaoming", "123456");
subject.login(token);
//是否认证
System.out.println("isAuthenticated:"+subject.isAuthenticated());
//用户角色多个角色查询
subject.checkRoles("user","admin");
//用户权限
subject.checkPermission("user:update");
}
}
自定义realm
package com.tmf.shiro.realm;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
public class CustomRealm extends AuthorizingRealm{
Map<String,String> userMap = new HashMap<String, String>();
{
userMap.put("mark", "73bea81c6c06bacab41a995495239545");
super.setName("customRealm");//设置realm名字
}
//做授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// TODO Auto-generated method stub
//1、认证信息中获取用户名
String usernaem = (String)principals.getPrimaryPrincipal();
//2、通过用户信息获取角色信息 从数据库或者缓存中获取角色数据
Set<String> roles = getRolesByName(usernaem);
//3、通过用户名获取用户权限信息
Set<String> permissions = getPermissionByName(usernaem);
//4、返回对象数据
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
authorizationInfo.setRoles(roles);
authorizationInfo.setStringPermissions(permissions);
return authorizationInfo;
}
private Set<String> getRolesByName(String username){
Set<String> sets = new HashSet<String>();
sets.add("user");
sets.add("admin");
return sets;
}
private Set<String> getPermissionByName(String username){
Set<String> sets = new HashSet<String>();
sets.add("user:delete");
sets.add("user:add");
return sets;
}
//做认证 token认证信息
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
// TODO Auto-generated method stub
//1、从主体传过来的认证信息中 获得用户名
String username = (String)token.getPrincipal();
//2、通过用户名到数据库中获取凭证
String password = getPasswordByUsername(username);
if(password == null) {
return null;
}
//3、创建返回对象
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo("mark",password,"customRealm");
//盐设置进去
authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes("mark"));
return authenticationInfo;
}
private String getPasswordByUsername(String username) {
return userMap.get(username);
}
public static void main(String[] args) {
//md5加密 以及加盐
Md5Hash md5 = new Md5Hash("123456","mark");
System.out.println(md5);
}
}
自定义realm测试
package com.tmf.test;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.subject.Subject;
import org.junit.Test;
import com.tmf.shiro.realm.Customrealm;
public class CustomRealmTest {
@Test
public void testAuthencation() {
Customrealm customrealm = new Customrealm();
//1.构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
defaultSecurityManager.setRealm(customrealm);
//2.主体提交认证请求
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("mark", "123456");
subject.login(token);
//是否认证
System.out.println("isAuthenticated:"+subject.isAuthenticated());
//用户角色
subject.checkRoles("user","admin");
//用户权限
subject.checkPermissions("user:delete","user:add");
}
}
Shiro加密
- shiro散列配置
- HashedCredentialsMatcher自定义Realm中使用散列盐的使用
- 打印出来
//加密
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("md5");//加密算法
matcher.setHashIterations(1);//加密次数
//md5加密 以及加盐
Md5Hash md5 = new Md5Hash("123456","mark");
System.out.println(md5);
授权方式
- 注解授权
- 编程授权
- 编程授权
if(subject.hasRole("admin"))
{
return "有admin角色";
}
return "无admin角色";
- Spring.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
<import resource="spring-dao.xml"/>
<context:component-scan base-package="com.tmf"/>
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- 配置对象 -->
<property name="securityManager" ref="securityManager"></property>
<property name="loginUrl" value="login.html"></property>
<property name="unauthorizedUrl" value="403.html"></property><!-- 没有权限的时候跳转的页面 -->
<!-- 过滤器链 -->
<property name="filterChainDefinitions">
<!-- shiro内置过滤器 -->
<value>
/login.html = anon <!-- 不需要认证-->
/subLogin = anon
<!--
/testRoles1 = roles["admin"]
/testRoles2 = roles["admin","admin1"]
/testPerms1 = perms["user:update"]
/testPerms2 = perms["user:delete","user:update"]
-->
/testRoles1 = rolesOr["admin","user","admin1"]<!-- 有其中一个就可以 -->
/testRoles2 = roles["admin","admin1"] <!-- 都得有才可以 -->
/* = authc <!-- 需要认证 -->
</value>
</property>
<property name="filters">
<util:map>
<entry key="rolesOr" value-ref="rolesOrFilter"></entry>
</util:map>
</property>
</bean>
<!-- 创建自定义的Filter -->
<bean class="com.tmf.filter.RolesOrFilter" id="rolesOrFilter" />
<!-- 创建SercurityManager对象 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="realm"></property>
</bean>
<!-- 创建自定义realm对象 -->
<bean id="realm" class="com.tmf.shiro.realm.CustomRealm">
<property name="credentialsMatcher" ref="credentialsMatcher"></property>
</bean>
<!-- 加密管理器对象 -->
<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
<property name="hashAlgorithmName" value="md5"/>
<property name="hashIterations" value="1"/>
</bean>
</beans>
- Spring-dao.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="jdbc:mysql://localhost:3306/test"/>
<property name="username" value="root"/>
<property name="password" value="tiger"/>
</bean>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
- Spring-mvc.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.tmf.controller"/>
<mvc:annotation-driven/>
<!-- 排除静态文件 -->
<mvc:resources mapping="/*" location="/"/>
</beans>
- 注解授权
- 授权通过才可以去访问url
@RequiresPermissions("user:delete") //访问的权限为user:delete才可以进去
@RequiresPermissions({"user:update","user:delete"})//访问的权限为user:delete和user:update才可以进去
@RequiresRoles("admin")//访问的用户为admin才可以进去
@RequiresRoles({"admin","user"})//访问的用户为admin和user才可以进去
Shiro过滤器
-
Shiro内置过滤器,访问问题
-
Anon 不需要认证直接访问
-
authBasic Http authBasice
-
Authc 需要认证后才可以访问
-
User 需要当前存在用户才可以访问
-
Logout 退出
-
授权相关问题
-
Perms 权限访问
-
Roles 角色访问
-
Ssl 安全协议访问
-
Port 端口访问
-
用户表,角色表 ,权限表
用户表---------用户名 密码
角色表---------用户名 角色名
权限信息表 -------角色名 权限名 -
自定义Filter
package com.tmf.filter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
/**
* @Package: com.tmf.filter
* @author:
* @date: 2018年12月24日
* 传入多个roles满足任何一个即可
* 授权相关 AuthorizationFilter
* 认证相关 AuthenticationFilter
*/
public class RolesOrFilter extends AuthorizationFilter {
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue)
throws Exception {
// TODO Auto-generated method stub
//获得到主体
Subject subject = getSubject(request, response);
String[] roles = (String[])mappedValue;
if(roles == null || roles.length ==0)//不需要任何角色
{
return true;
}
for(String role : roles) { //拥有其中一个角色
if(subject.hasRole(role)) {
return true;
}
}
return false;
}
}
- spring中需要添加配置
<property name="filters">
<util:map>
<entry key="rolesOr" value-ref="rolesOrFilter"></entry>
</util:map>
</property>
</bean>
<!-- 创建自定义的Filter -->
<bean class="com.tmf.filter.RolesOrFilter" id="rolesOrFilter" />
Shiro会话管理
- Shiro Session
SessionManager,SessionDAO - Redis实现Session共享
Redis实现Session共享可能出现的问题
Shiro缓存管理
-
缓存角色数据和权限数据 每次查询时直接到缓存中去 不用再去数据库中查询,主要做授权缓存CacheManager ,Cache,Redis实现 CacheManager
-
REmote DIctionary Server(Redis) 是一个由Salvatore Sanfilippo写的key-value存储系统。
-
Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。
它通常被称为数据结构服务器,因为值(value)可以是 字符串(String), 哈希(Map), 列表(list), 集合(sets) 和 有序集合(sorted sets)等类型。 -
@component (把普通pojo实例化到spring容器中,相当于配置文件中的)
从redis中获取权限数据 ------------redis中先没有权限数据 -
从数据库中获取授权数据----------------取到之后再放到redis中
再次访问
从redis中获取权限数据 ------------redis中已经有了数据
//本地二级缓存,提升程序性能 -
Shiro RememberMe 记住密码 自动登录
<!-- 创建SercurityManager对象 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="realm"></property>
<property name="sessionManager" ref="sessionManager"></property>
<property name="cacheManager" ref="cacheManager"></property>
<property name="rememberMeManager" ref="cookieRememberMeManager"></property>
</bean>
- Property 中的name是 SecurityManager中特有的属性名字。ref就是其他配置bean的连接
- sessionDao和sessionManager是用于每次去读取角色信息和权限信息不用每次都去数据库去读取信息,而是从redis中去取,就是做缓存处理的。这样能提升程序的性能。