本文共 18945 字,大约阅读时间需要 63 分钟。
① 认识shiro的整体架构,各组件的概念
② shiro认证,授权的过程
③ shiro自定义的realm、Filter
④ shiro session管理
⑤ shiro的缓存管理
⑥ shiro继承spring
① shiro是Apache提供的一个强大灵活的安全框架
② shiro提供了认证、授权、企业会话管理、加密、缓存管理相关的功能,使用shiro可以非常方便的完成项目的权限管理模块开发
① Apache shiro: 简单灵活、可脱离spring、粒度较粗
② spring Security: 复杂笨重、不可脱离spring、粒度更细
①上面标记为1的是shiro的主体部分subject,可以理解为当前的操作用户
② Security Manager为Shiro的核心,shiro是通过security Manager来提供安全服务的,security Manager管理着Session Manager、Cache Manager等其他组件的实例:Authenticator(认证器,管理我们的登录登出) Authorizer(授权器,负责赋予主体subject有哪些权限) Session Manager(shiro自己实现的一套session管理机制,可以不借助任何web容器的情况下使用session) Session Dao(提供了session的增删改查操作) cache Manager(缓存管理器,用于缓存角色数据和权限数据) Pluggable Realms(shiro与数据库/数据源之间的桥梁,shiro获取认证信息、权限数据、角色数据都是通过Realms来获取)
③ 上图标记为2的cryptography是用来做加密的,使用它可以非常方便快捷的进行数据加密。
④ 上面箭头的流程可以这样理解:主体提交请求到Security Manager,然后由Security Manager调用Authenticator去做认证,而Authenticator去获取认证数据的时候是通过Realms从数据源中来获取的,然后把从数据源中拿到的认证信息与主体提交过来的认证信息做比对。授权器Authorizer也是一样。
① 创建Security Manager:Security Manager是用来提供安全服务的,所以在做shiro认证的时候要先创建此对象
② 主体Subject提交请求给Security Manager
③ Security Manager调用Authenticator组件做认证
④Authenticator通过Realm来从数据源中获取认证数据
***以下通过代码来演示shiro是如何做认证的***
① 引入依赖
org.apache.shiro shiro-core 1.4.0 junit junit RELEASE
② 使用示例
package com.imooc.test;import org.apache.shiro.SecurityUtils;import org.apache.shiro.authc.AuthenticationToken;import org.apache.shiro.authc.UsernamePasswordToken;import org.apache.shiro.mgt.DefaultSecurityManager;import org.apache.shiro.realm.SimpleAccountRealm;import org.apache.shiro.subject.Subject;import org.junit.Before;import org.junit.Test;/** * @auther xiehuaxin * @create 2018-07-02 14:06 * @todo 创建一个测试类,测试认证 */public class AuthenticationTest { //除了SimpleAccountRealm还有JdbcRealm等 SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm(); //在认证之前先在Realm中添加一个用户,创建Security Manager的时候要用到Realm @Before public void addUser() { simpleAccountRealm.addAccount("xiehuaxin","123456"); } @Test public void testAutentication() { //1.构建Security Manager环境(Security Manager是用来提供安全服务的,所以在做shiro认证的时候要先创建此对象,创建Security Manager对象之后要设置Realm) DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager(); defaultSecurityManager.setRealm(simpleAccountRealm); //2.获取向Security Manager提交请求的subject,而主体subject可以通过shiro提供的一个工具类SecurityUtils来获取 SecurityUtils.setSecurityManager(defaultSecurityManager);//使用SecurityUtils之前要设置Security Manager环境 Subject subject = SecurityUtils.getSubject(); //3.主体Subject提交请求给Security Manager --> subject.login(token); UsernamePasswordToken token = new UsernamePasswordToken("xiehuaxin","123456");//提交请求时需要一个token,所以要先创建token subject.login(token); //4. shiro提供了一个检查主体subject是否认证的方法isAuthenticated(),此方法的返回结果是一个boolean值 System.out.println(subject.isAuthenticated()); subject.logout(); System.out.println(subject.isAuthenticated()); }}
六、Shiro的授权(shiro授权过程与认证过程基本一致)
① 创建Security Manager
② 主体subject授权
③ 主体授权是交给Security Manager授权
④ Security Manager调用授权器Authorizer授权
⑤ 通过Realm在数据库或者缓存中来获取授权的数据(角色数据和权限数据)
***以下通过代码来演示shiro是如何做授权的***
package com.imooc.test;import org.apache.shiro.SecurityUtils;import org.apache.shiro.authc.AuthenticationToken;import org.apache.shiro.authc.UsernamePasswordToken;import org.apache.shiro.mgt.DefaultSecurityManager;import org.apache.shiro.realm.SimpleAccountRealm;import org.apache.shiro.subject.Subject;import org.junit.Before;import org.junit.Test;/** * @auther xiehuaxin * @create 2018-07-02 14:06 * @todo 创建一个测试类,测试认证 */public class AuthenticationTest { SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm(); @Before public void addUser() { //添加用户的时候为此用户添加角色,一个用户可以拥一个或多个角色 simpleAccountRealm.addAccount("xiehuaxin", "123456", "admin", "user"); } @Test public void testAutentication() { //1.构建Security Manager环境(Security Manager是用来提供安全服务的,所以在做shiro认证的时候要先创建此对象,创建Security Manager对象之后要设置Realm) DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager(); defaultSecurityManager.setRealm(simpleAccountRealm); //2.获取向Security Manager提交请求的subject,而主体subject可以通过shiro提供的一个工具类SecurityUtils来获取 SecurityUtils.setSecurityManager(defaultSecurityManager);//使用SecurityUtils之前要设置Security Manager环境 Subject subject = SecurityUtils.getSubject(); //3.主体Subject提交请求给Security Manager --> subject.login(token); UsernamePasswordToken token = new UsernamePasswordToken("xiehuaxin", "123456");//提交请求时需要一个token,所以要先创建token subject.login(token); //4. shiro提供了一个检查主体subject是否认证的方法isAuthenticated(),此方法的返回结果是一个boolean值 System.out.println(subject.isAuthenticated()); //校验角色 subject.checkRoles("admin"); }}
1. 在说自定义Realm之前先讲一下Shiro的内置Realm
① IniRealm
a) 在项目中新建一个ini文件,下面的内容的意思是,新建了一个用户,账号是xiehuaxin,密码为123456,此账号的角色为admin,而admin角色又具有删除用户和更新用户的权限(多个权限中间用逗号分隔),注意这里的“user:delete”不是一定要这样写的,只是一个字符串,你也可以写成"admin=shanchuyonghu",这要在校验的时候也用“shanchuyonghu”来匹配就行了,当然这种命名习惯还是不建议的,建议使用“user:delete”这种对象+动作的命名方式。
[users]xiehuaxin=123456,admin[roles]admin=user:delete,user:update
package com.imooc.test;import org.apache.shiro.SecurityUtils;import org.apache.shiro.authc.UsernamePasswordToken;import org.apache.shiro.mgt.DefaultSecurityManager;import org.apache.shiro.realm.SimpleAccountRealm;import org.apache.shiro.realm.text.IniRealm;import org.apache.shiro.subject.Subject;import org.junit.Before;import org.junit.Test;/** * @auther xiehuaxin * @create 2018-07-02 14:47 * @todo */public class IniRealmTest { @Test public void testAutentication() { IniRealm iniRealm = new IniRealm("classpath:user.ini"); //1.构建Security Manager环境(Security Manager是用来提供安全服务的,所以在做shiro认证的时候要先创建此对象,创建Security Manager对象之后要设置Realm) DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager(); defaultSecurityManager.setRealm(iniRealm); //2.获取向Security Manager提交请求的subject,而主体subject可以通过shiro提供的一个工具类SecurityUtils来获取 SecurityUtils.setSecurityManager(defaultSecurityManager);//使用SecurityUtils之前要设置Security Manager环境 Subject subject = SecurityUtils.getSubject(); //3.主体Subject提交请求给Security Manager --> subject.login(token); UsernamePasswordToken token = new UsernamePasswordToken("xiehuaxin","123456");//提交请求时需要一个token,所以要先创建token subject.login(token); //4. shiro提供了一个检查主体subject是否认证的方法isAuthenticated(),此方法的返回结果是一个boolean值 System.out.println(subject.isAuthenticated()); subject.checkRoles("admin"); subject.checkPermission("user:delete"); }}
② JdbcRealm
a) 引入数据库相关依赖
mysql mysql-connector-java 5.1.45 com.alibaba druid 1.1.4
package com.imooc.test;import com.alibaba.druid.pool.DruidDataSource;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;/** * @auther xiehuaxin * @create 2018-07-02 15:16 * @todo */public class JdbcRealmTest { DruidDataSource dataSource = new DruidDataSource(); { dataSource.setUrl("jdbc:mysql://localhost:3306/test"); dataSource.setUsername("root"); dataSource.setPassword("123123"); } @Test public void testAutentication() { //1.创建jdbcReaml对象 JdbcRealm jdbcRealm = new JdbcRealm(); //2.设置数据源 jdbcRealm.setDataSource(dataSource); //这里需要注意的是,在使用JdbcRealm的时候要设置权限的开关,只有设置为true时才会去查询权限数据 jdbcRealm.setPermissionsLookupEnabled(true); //使用自定义的表/sql String sql = "select password from test_user where user_name = ?"; jdbcRealm.setAuthenticationQuery(sql); String roleSql = "select role_name from test_user_role where user_name = ?"; jdbcRealm.setUserRolesQuery(roleSql); //1.构建Security Manager环境(Security Manager是用来提供安全服务的,所以在做shiro认证的时候要先创建此对象,创建Security Manager对象之后要设置Realm) DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager(); defaultSecurityManager.setRealm(jdbcRealm); //2.获取向Security Manager提交请求的subject,而主体subject可以通过shiro提供的一个工具类SecurityUtils来获取 SecurityUtils.setSecurityManager(defaultSecurityManager);//使用SecurityUtils之前要设置Security Manager环境 Subject subject = SecurityUtils.getSubject(); //3.主体Subject提交请求给Security Manager --> subject.login(token); UsernamePasswordToken token = new UsernamePasswordToken("xiehuaxin","123456");//提交请求时需要一个token,所以要先创建token subject.login(token); // 4. shiro提供了一个检查主体subject是否认证的方法isAuthenticated(),此方法的返回结果是一个boolean值 System.out.println(subject.isAuthenticated()); }}
在使用JdbcRealm时,如果没有设置查询语句,JdbcReal有自己默认的查询语句。
通过查看JdbcRealm或者IniRealm的源码可以发现,它们都继承自AuthorizingRealm,所以要自定义Realm也要使自定义的Realm继承AuthorizingRealm.
package com.imooc.test;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.realm.AuthorizingRealm;import org.apache.shiro.subject.PrincipalCollection;import java.util.HashMap;import java.util.HashSet;import java.util.Map;import java.util.Set;/** * @auther xiehuaxin * @create 2018-07-02 15:48 * @todo */public class CustomRealm extends AuthorizingRealm { MapuserMap = new HashMap (16); { userMap.put("xiehuaxin","123456"); super.setName("customReal");//自定义 } /** * 用来做授权(就是checkRole,checkPermission时用到的) * @param principalCollection * @return */ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //1.先获取到用户名 String userName = (String) principalCollection.getPrimaryPrincipal(); //2.从数据库或者缓存中获取角色数据 Set roles = getRolesByUserName(userName); //3.从数据库或缓存中获取权限数据 Set permissions = getPermissionsByUserName(userName); SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); simpleAuthorizationInfo.setStringPermissions(permissions); simpleAuthorizationInfo.setRoles(roles); return simpleAuthorizationInfo; } /** * 从数据库或缓存中获取权限数据(这里模拟数据库查询) * @param userName * @return */ private Set getPermissionsByUserName(String userName) { Set sets = new HashSet (); sets.add("user:delete"); sets.add("user:add"); return sets; } /** * 通过用户名获取到角色数据(这里为了简单点就不真的去查询数据库了,仅模拟数据库查询) * @param userName * @return */ private Set getRolesByUserName(String userName) { Set sets = new HashSet (); sets.add("admin"); sets.add("user"); return sets; } /** * 用来做认证(就是login时用到的) * @param authenticationToken 主体subject传过来的验证信息 * @return * @throws AuthenticationException */ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //1.先通过主体传过来的验证信息获取用户名 String userName = (String) authenticationToken.getPrincipal(); //2.通过用户名去数据库中获取凭证 String password = getPasswordByUserName(userName); if(password == null) { return null; } //查询到用户,则返回AuthenticationInfo对象 SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo("xiehuaxin",password,"customRealm"); return authenticationInfo; } private String getPasswordByUserName(String userName) { //这里我就不写查询数据库了,就模拟去查数据库 return userMap.get(userName); }}
测试自定义Realm
package com.imooc.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;/** * @auther xiehuaxin * @create 2018-07-02 16:02 * @todo */public class CustomRealmTest { @Test public void testAutentication() { CustomRealm customRealm = new CustomRealm(); //1.构建Security Manager环境(Security Manager是用来提供安全服务的,所以在做shiro认证的时候要先创建此对象,创建Security Manager对象之后要设置Realm) DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager(); defaultSecurityManager.setRealm(customRealm); //2.获取向Security Manager提交请求的subject,而主体subject可以通过shiro提供的一个工具类SecurityUtils来获取 SecurityUtils.setSecurityManager(defaultSecurityManager);//使用SecurityUtils之前要设置Security Manager环境 Subject subject = SecurityUtils.getSubject(); //3.主体Subject提交请求给Security Manager --> subject.login(token); UsernamePasswordToken token = new UsernamePasswordToken("xiehuaxin","123456");//提交请求时需要一个token,所以要先创建token subject.login(token); //4. shiro提供了一个检查主体subject是否认证的方法isAuthenticated(),此方法的返回结果是一个boolean值 System.out.println(subject.isAuthenticated()); subject.checkRoles("admin"); subject.checkPermission("user:delete"); }}
package com.imooc.test;import org.apache.shiro.SecurityUtils;import org.apache.shiro.authc.UsernamePasswordToken;import org.apache.shiro.authc.credential.HashedCredentialsMatcher;import org.apache.shiro.mgt.DefaultSecurityManager;import org.apache.shiro.realm.text.IniRealm;import org.apache.shiro.subject.Subject;import org.junit.Test;/** * @auther xiehuaxin * @create 2018-07-02 16:02 * @todo */public class CustomRealmTest { @Test public void testAutentication() { CustomRealm customRealm = new CustomRealm(); //1.构建Security Manager环境(Security Manager是用来提供安全服务的,所以在做shiro认证的时候要先创建此对象,创建Security Manager对象之后要设置Realm) DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager(); defaultSecurityManager.setRealm(customRealm); HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(); matcher.setHashAlgorithmName("md5");//设置加密算法名称 matcher.setHashIterations(1);//设置加密的次数 customRealm.setCredentialsMatcher(matcher); //2.获取向Security Manager提交请求的subject,而主体subject可以通过shiro提供的一个工具类SecurityUtils来获取 SecurityUtils.setSecurityManager(defaultSecurityManager);//使用SecurityUtils之前要设置Security Manager环境 Subject subject = SecurityUtils.getSubject(); //3.主体Subject提交请求给Security Manager --> subject.login(token); UsernamePasswordToken token = new UsernamePasswordToken("xiehuaxin","123456");//提交请求时需要一个token,所以要先创建token subject.login(token); //4. shiro提供了一个检查主体subject是否认证的方法isAuthenticated(),此方法的返回结果是一个boolean值 System.out.println(subject.isAuthenticated()); subject.checkRoles("admin"); subject.checkPermission("user:delete"); }}
package com.imooc.test;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.realm.AuthorizingRealm;import org.apache.shiro.subject.PrincipalCollection;import org.apache.shiro.util.ByteSource;import java.util.HashMap;import java.util.HashSet;import java.util.Map;import java.util.Set;/** * @auther xiehuaxin * @create 2018-07-02 15:48 * @todo */public class CustomRealm extends AuthorizingRealm { MapuserMap = new HashMap (16); { userMap.put("xiehuaxin","123456"); super.setName("customReal");//自定义 } /** * 用来做授权(就是checkRole,checkPermission时用到的) * @param principalCollection * @return */ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //1.先获取到用户名 String userName = (String) principalCollection.getPrimaryPrincipal(); //2.从数据库或者缓存中获取角色数据 Set roles = getRolesByUserName(userName); //3.从数据库或缓存中获取权限数据 Set permissions = getPermissionsByUserName(userName); SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); simpleAuthorizationInfo.setStringPermissions(permissions); simpleAuthorizationInfo.setRoles(roles); return simpleAuthorizationInfo; } /** * 从数据库或缓存中获取权限数据(这里模拟数据库查询) * @param userName * @return */ private Set getPermissionsByUserName(String userName) { Set sets = new HashSet (); sets.add("user:delete"); sets.add("user:add"); return sets; } /** * 通过用户名获取到角色数据(这里为了简单点就不真的去查询数据库了,仅模拟数据库查询) * @param userName * @return */ private Set getRolesByUserName(String userName) { Set sets = new HashSet (); sets.add("admin"); sets.add("user"); return sets; } /** * 用来做认证(就是login时用到的) * @param authenticationToken 主体subject传过来的验证信息 * @return * @throws AuthenticationException */ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //1.先通过主体传过来的验证信息获取用户名 String userName = (String) authenticationToken.getPrincipal(); //2.通过用户名去数据库中获取凭证 String password = getPasswordByUserName(userName); if(password == null) { return null; } //查询到用户,则返回AuthenticationInfo对象 SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo("xiehuaxin",password,"customRealm"); authenticationInfo.setCredentialsSalt(ByteSource.Util.bytes("xie"));//这里要把盐也返回(如果密码做加密处理的时候也加了盐的话),真实项目中这里不该写死 return authenticationInfo; } private String getPasswordByUserName(String userName) { //这里我就不写查询数据库了,就模拟去查数据库 return userMap.get(userName); }}