shiro快速入门
来源:互联网 发布:关于套路的网络用语 编辑:程序博客网 时间:2024/06/05 22:50
传统方案:通过设置拦截器,基于url的方式进行管理,创建一个user类,用于存储menus,把user存储到session中到前端进行菜单动态显示,而user类的permissions集合用于url拦截,有对应权限才放行。这种方式实现简单,但是不易于维护。
新方案:使用shiro权限管理框架
什么是shiro?
Shiro是apache麾下的开源java安全框架,提供了认证、授权、加密、会话管理、与Web集成、缓存等功能,与此类似的框架还有spring security。
shiro功能图
subject:主体,可以是用户也可以是程序,主体要访问系统,系统需要对主体进行认证、授权。
securityManager:安全管理器,主体进行认证和授权都是通过securityManager进行,是shiro的核心。
authenticator:认证器,主体进行认证最终通过authenticator进行的。
authorizer:授权器,主体进行授权最终通过authorizer进行的。
sessionManager:web应用中一般是用web容器对session进行管理,shiro也提供一套session管理的方式。
SessionDao:通过SessionDao管理session数据,针对个性化的session数据存储需要使用sessionDao。
cache Manager:缓存管理器,主要对session和授权数据进行缓存,比如将授权数据通过cacheManager进行缓存管理,和ehcache整合对缓存数据进行管理。
realm:域,领域,相当于数据源,通过realm存取认证、授权相关数据。
shiro架构图
subject:主体,可以是用户也可以是程序,主体要访问系统,系统需要对主体进行认证、授权。
securityManager:安全管理器,主体进行认证和授权都是通过securityManager进行,是shiro的核心。
authenticator:认证器,主体进行认证最终通过authenticator进行的。
authorizer:授权器,主体进行授权最终通过authorizer进行的。
sessionManager:web应用中一般是用web容器对session进行管理,shiro也提供一套session管理的方式。
SessionDao:通过SessionDao管理session数据,针对个性化的session数据存储需要使用sessionDao。
cache Manager:缓存管理器,主要对session和授权数据进行缓存,比如将授权数据通过cacheManager进行缓存管理,和ehcache整合对缓存数据进行管理。
realm:域,领域,相当于数据源,通过realm存取认证、授权相关数据。
示例1
下面是一个认证和授权的入门例子,只需要一个shiro-core的jar包和它所依赖的slf4j-api.jar、commons-logging、
commons-beanutils.jar、hamcrest-core.jar以及Junit,使用maven依赖的话,只要添加shiro-core、commons-logging和Junit就行了,
我使用的是shiro1.3.2。
下面这个例子所用到的数据都在shiro-data-from-ini.ini文件里,模拟数据库的数据,一般是测试用的,真正的项目肯定是自定义realm,然后到数据库中去获取用户信息和权限信息的,例子中对主要步骤都有注释。
shiro-data-from-ini.ini
#模拟用户数据源,帐号=密码,角色,角色...(有realm时不再起作用)[users]pens=123,role1,role2holien=123,role3#设置角色、权限和资源(有realm时不再起作用)#格式:角色=资源:操作:实例,资源:操作 相当于 资源:操作:*[roles]#角色role1对资源user拥有create、update权限role1=user:create,user:update#角色role2对资源user拥有create、delete权限role2=user:delete#角色role3对资源user拥有create权限role3=user:create#以上的数据在没有设置realm时才起作用
测试类:AuthcAndAuthzByIniData.class
package shiro_authenc;import org.apache.shiro.SecurityUtils;import org.apache.shiro.authc.UsernamePasswordToken;import org.apache.shiro.config.IniSecurityManagerFactory;import org.apache.shiro.mgt.SecurityManager;import org.apache.shiro.subject.Subject;import org.junit.Test;import java.util.Arrays;import java.util.List;/** * writer: holien * Time: 2017-08-19 20:30 * Intent: 使用ini文件中测试数据作为数据源进行身份认证和授权 */public class AuthcAndAuthzByIniData { @Test public void testAuthcAndAuthozByRealm() { /* 身份认证 */ IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro-data-from-ini.ini"); SecurityManager securityManager = factory.getInstance(); // 把安全管理器与安全工具关联起来 SecurityUtils.setSecurityManager(securityManager); // 模拟用户表单发送过来的帐号密码,默认名称必须是username和password,可以在配置文件更改 String username = "pens"; String password = "123"; // 创建一个口令(用户输入的帐号密码),帐号信息与ini文件中的帐号信息匹配即可认证成功 UsernamePasswordToken token = new UsernamePasswordToken(username, password); // 获取用户主体 Subject subject = SecurityUtils.getSubject(); try { // 登录,即身份认证(另起一个线程执行此方法),到ini文件中去对比用户信息 subject.login(token); } catch (Exception e) { e.printStackTrace(); } System.out.println("是否认证:" + subject.isAuthenticated()); // 退出后再判断,无论认证还是授权都是false// subject.logout();// System.out.println("是否认证:" + subject.isAuthenticated()); /* 基于角色的授权 */ // 验证是否具有指定角色 System.out.println("是否具有角色role1:" + subject.hasRole("role1")); // 验证是否具有其中某个角色(这里我觉得使用多个hasRole方法进行逻辑判断比较直观) List<String> list = Arrays.asList("role1", "role2"); boolean[] b = subject.hasRoles(list); System.out.println(b[0] + " " + b[1]); // 验证是否具有某组角色,需要多个角色一起拥有 System.out.println("是否同时具有角色role1和role2:" + subject.hasAllRoles(Arrays.asList("role1", "role2"))); /* 基于资源的授权 */ System.out.println("是否具有对用户的创建权限:" + subject.isPermitted("user:create")); System.out.println("是否同时具有对用户的创建、更新、删除权限:" + subject.isPermittedAll("user:create", "user:update", "user:delete")); // 与isPermission不同,checkPermisson如果检测不到相应的权限会抛异常 subject.checkPermission("user:create");// subject.checkPermission("user:batch"); // 抛出异常 }}这里先看看shiro自带的md5加密的基本使用例子,下面会使用到
package shiro_authenc;import org.apache.shiro.crypto.hash.Md5Hash;import org.junit.Test;/** * writer: holien * Time: 2017-08-20 20:38 * Intent: shiro自带的MD5加密 */public class MD5Test { @Test public void testMD5() { String password = "123"; // MD5加密加盐 String salt = "110"; // 参数1:待加密密码 参数2:盐 参数3:hash迭代次数 Md5Hash md5Hash = new Md5Hash(password, salt, 1); String md5Password = md5Hash.toString(); System.out.println(md5Password); // 5319bf4ef8f5029ec32a4ad62a3f8eff }}
示例2
shiro-realm.ini
[main]#定义凭证(密码)匹配器credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher#指定散列算法credentialsMatcher.hashAlgorithmName=md5#散列次数credentialsMatcher.hashIterations=1#设置自定义的realmcustomRealm=shiro_authenc.CustomRealm#将凭证匹配器设置到realm中customRealm.credentialsMatcher=$credentialsMatchersecurityManager.realms=$customRealm
此ini文件定义了凭证匹配器,使用了md5算法,迭代1次;自定义realm类的引用配置到名为customRealm的realm,并把前面定义的凭证匹配器配置给customRealm,最后把customRealm配置到securityManager中。
自定义realm,继承自AuthorizingRealm的CustomRealm.classpackage shiro_authenc;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.ArrayList;import java.util.Arrays;/** * writer: holien * Time: 2017-08-20 14:47 * Intent: 自定义realm,其中的用户信息需要到数据库中查询,在这里使用测试数据, * 配置了realm就不会再读取ini文件的user了 */public class CustomRealm extends AuthorizingRealm { // 授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { // 认证成功后,用户身份会被存进principalCollection,我们根据主用户身份去数据库取相应的角色和权限 String username = (String) principalCollection.getPrimaryPrincipal(); // 模拟从数据库取出的资源权限,格式:资源:操作:实例 ArrayList<String> permissions = new ArrayList<>(); permissions.add("user:create"); permissions.add("user:update"); permissions.add("user:delete"); // 把数据库取出的角色或权限存入info对象中,供授权器校验 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.addStringPermissions(permissions); info.addRoles(Arrays.asList("role1", "role2")); return info; // 若查询不到对应的角色或权限,则返回null } // 认证 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { // 从token取出用户输入的帐号,根据此帐号去数据库取帐号,下面以userCode来模拟 String username = (String) authenticationToken.getPrincipal(); // 根据用户输入的帐号从数据库取出帐号,我们假设取出的帐号叫userCode String userCode = "pens"; if (userCode == null || userCode.equals("")) { // 如果帐号不存在,返回null return null; } else { // 模拟根据帐号从数据库取出的散列密码(md5散列一次,盐为110),查不到返回null,查得到返回info String password = "5319bf4ef8f5029ec32a4ad62a3f8eff"; // 从数据库获取盐,假设为110 String salt = "110"; // realm会根据此对象包含的password(数据库)与token中的password(用户输入的密码进行加盐加密)做对比,加密算法在ini文件中指定 SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username, password, ByteSource.Util.bytes(salt), this.getName()); return info; } }}测试类:testAuthcAndAuthozByRealm.class
package shiro_authenc;import org.apache.shiro.SecurityUtils;import org.apache.shiro.authc.UsernamePasswordToken;import org.apache.shiro.config.IniSecurityManagerFactory;import org.apache.shiro.mgt.SecurityManager;import org.apache.shiro.subject.Subject;import org.junit.Test;import java.util.Arrays;import java.util.List;/** * writer: holien * Time: 2017-08-19 20:30 * Intent: 使用realm作为数据源进行身份认证和授权 */public class AuthcAndAuthzByRealm { @Test public void testAuthcAndAuthozByRealm() { /* 身份认证 */ IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro-realm.ini"); SecurityManager securityManager = factory.getInstance(); // 把安全管理器与安全工具关联起来 SecurityUtils.setSecurityManager(securityManager); // 模拟用户表单发送过来的帐号密码,默认名称必须是username和password,可以在配置文件更改 String username = "pens"; String password = "123"; // 创建一个口令(用户输入的帐号密码),帐号信息与realm去数据库中获取的帐号信息相同即可认证成功 UsernamePasswordToken token = new UsernamePasswordToken(username, password); // 获取用户主体 Subject subject = SecurityUtils.getSubject(); try { // 登录,即身份认证(另起一个线程执行此方法),到realm中去对比用户信息 subject.login(token); } catch (Exception e) { e.printStackTrace(); } System.out.println("是否认证:" + subject.isAuthenticated()); // 退出后再判断,无论认证还是授权都是false// subject.logout();// System.out.println("是否认证:" + subject.isAuthenticated()); /* 基于角色的授权 */ // 验证是否具有指定角色 System.out.println("是否具有角色role1:" + subject.hasRole("role1")); // 验证是否具有其中某个角色(这里我觉得使用多个hasRole方法进行逻辑判断比较直观) List<String> list = Arrays.asList("role1", "role2"); boolean[] b = subject.hasRoles(list); System.out.println(b[0] + " " + b[1]); // 验证是否具有某组角色,需要多个角色一起拥有 System.out.println("是否同时具有角色role1和role2:" + subject.hasAllRoles(Arrays.asList("role1", "role2"))); /* 基于资源的授权 */ System.out.println("是否具有对用户的创建权限:" + subject.isPermitted("user:create")); System.out.println("是否同时具有对用户的创建、更新、删除权限:" + subject.isPermittedAll("user:create", "user:update", "user:delete")); // 与isPermission不同,checkPermisson如果检测不到相应的权限会抛异常 subject.checkPermission("user:create");// subject.checkPermission("user:batch"); // 抛出异常 }}其中login方法一经调用就会到customRealm中的doGetAuthenticationInfo方法去验证用户信息,而hasRole方法和isPermitted等方法一经调用都会到customRealm中的doGetAuthorizationInfo方法去查询,这里有点耗资源,等spring整合时,使用ehcache做缓存,就不用每次都到customRealm中获取了,直接返回缓存的结果。
总结一下流程
认证流程:
1、subject(主体)请求认证,调用subject.login(token)
2、SecurityManager (安全管理器)执行认证
3、SecurityManager通过ModularRealmAuthenticator进行认证。
4、ModularRealmAuthenticator将token传给realm,realm根据token中用户信息从数据库查询用户信息(包括身份和凭证)
5、realm如果查询不到用户给ModularRealmAuthenticator返回null,ModularRealmAuthenticator抛出异常(用户不存在)
6、realm如果查询到用户给ModularRealmAuthenticator返回AuthenticationInfo(认证信息)
7、ModularRealmAuthenticator拿着AuthenticationInfo(认证信息)去进行凭证(密码 )比对。如果一致则认证通过,如果不一致抛出异常(凭证错误)。
授权流程:
1、对subject进行授权,调用方法isPermitted("permission串")
2、SecurityManager执行授权,通过ModularRealmAuthorizer执行授权
3、ModularRealmAuthorizer执行realm(自定义的CustomRealm)中的doGetAuthorizationInfo方法从数据库查询权限数据
4、realm从数据库查询权限数据,返回给ModularRealmAuthorizer
5、ModularRealmAuthorizer调用PermissionResolver进行权限串比对
6、如果比对后,isPermitted中"permission串"在realm查询到权限数据中,说明用户访问permission串有权限,否则 没有权限,抛出异常。
下一篇就是shiro和spring的整合了,不能再拖拖拉拉了,加油...
- shiro快速入门
- Shiro快速入门
- Shiro快速入门
- Shiro快速入门
- shiro快速入门
- shiro权限框架简单快速入门
- shiro权限框架简单快速入门
- 关于shiro权限框架简单快速入门
- Shiro第四篇【Shiro与Spring整合、快速入门、Shiro过滤器、登陆认证】
- shiro入门
- shiro入门
- Shiro入门
- shiro入门
- shiro入门
- Shiro 入门
- Shiro 入门
- Shiro 入门
- Shiro 入门
- 深度学习中的代码资源库------图像处理篇
- 求1到20的阶乘的和 以及 1到21的阶乘的和,使用long和BigInteger
- sql日期格式化
- ORACLE冷备文件恢复
- ArrayBlockingQueue源码分析
- shiro快速入门
- 解决websocket中localhost改为ip地址连接不上
- Springcloud框架搭建
- 连接企业无线网络时需要频繁输入用户密码的解决办法
- latex中局部使用中文环境
- 汇总统计数据工具—你所不知道的arcgis工具
- Why is ARKit better than the alternatives?
- Python网络数据采集——正则表达式
- 53. Maximum Subarray (dp)