0


Apache shiro 权限框架

Apache shiro学习文档

在这里插入图片描述
作者:pinkhub

时间:2021/12/24

技术栈:springboot+mybatisPlus+shiro

未来,我们抱团取暖,结伴而行…


文档目录


第一章 Apache Shiro 概述

Apache Shiro 是一个强大而灵活的开源安全框架,可以干净地处理身份验证、授权、企业会话管理和加密。

身份验证:有时也被称为“登录”,这是一种证明用户真实身份的行为。

授权:访问控制的过程,即确定“谁”可以访问“什么”。

会话管理:管理特定于用户的会话,即使是在非 Web 或 EJB 应用程序中。

密码学:使用密码算法确保数据安全,同时仍然易于使用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zYBVONip-1640335050059)(images/ShiroFeatures.png)]
官方文档:https://shiro.apache.org/reference.html

第二章 Apache Shiro 架构

关键字:

  1. Subject

  1. SecurityManager````Realms

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IpntgpnB-1640335050060)(images/image-20211206145455917.png)]

第三章 Apache Shiro 配置

3.1 配置文件(ini)配置

基于INI配置使DefinitionRealm、definitionRealm生效

  1. # =======================
  2. # Shiro INI configuration
  3. # =======================
  4. [main]
  5. # Objects and their properties are defined here,
  6. # Such as the securityManager, Realms and anything
  7. # else needed to build the SecurityManager
  8. definitionRealm=edu.pinkhub.shrio_demo.realm.DefinitionRealm
  9. securityManager.realms=$definitionRealm
  10. [users]
  11. # The 'users' section is for simple deployments
  12. # when you only need a small number of statically-defined
  13. # set of User accounts.
  14. jay=1234
  15. [roles]
  16. # The 'roles' section is for simple deployments
  17. # when you only need a small number of statically-defined
  18. # roles.
  19. [urls]
  20. # The 'urls' section is used for url-based security
  21. # in web applications. We'll discuss this section in the
  22. # Web documentation

3.2 配置类配置

  1. @ConfigurationpublicclassShiroConfig{/**
  2. * 创建realm、securityManagert组件交给spring容器管理 等价于shiro.ini配置文件
  3. * @return
  4. */@Bean("shiroRealm")publicShiroRealmshiroRealm(){returnnewShiroRealm();}@Bean(name ="securityManager")publicDefaultWebSecurityManagerdefaultWebSecurityManager(@Qualifier("shiroRealm")ShiroRealm shiroRealm){DefaultWebSecurityManager securityManager =newDefaultWebSecurityManager();
  5. securityManager.setRealm(shiroRealm);return securityManager;}}

第四章 Apache Shiro 核心

前提

导入shiro依赖:可以选择

  1. shiro-spring-boot-starter

  1. shiro-spring

  1. shiro-core

的一个。

  1. <dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring-boot-starter</artifactId><version>1.8.0</version></dependency>

tips:

如果选择shiro-core依赖时,版本不要太高,否则

  1. IniSecurityManagerFactory

方法过时

4.1 编码和解码算法

shiro提供base6416进制字符串编码和解码的API支持。

工具类如下:

  1. publicclassEncodesUtil{/**
  2. * 利用Hex字节转字符串
  3. * @param input 输入字节数组
  4. * @return 字符串
  5. */publicstaticStringencodeHex(byte[] input){returnHex.encodeToString(input);}/**
  6. * 利用Hex字符串转字节
  7. * @param input 输入字符串
  8. * @return 字节数组
  9. */publicstaticbyte[]decodeHex(String input){returnHex.decode(input);}/**
  10. * 利用Base64格式将字节转字符串
  11. * @param input
  12. * @return
  13. */publicstaticStringencodeBase64(byte[] input){returnBase64.encodeToString(input);}/**
  14. * 利用Base64格式将字符串转字节
  15. * @param input
  16. * @return
  17. */publicstaticbyte[]decodeBase64(String input){returnBase64.decode(input);}}

4.2 散列算法

散列算法用于生成数据的摘要信息,不可逆算法,常用于存储密码,常见的散列算法有:MD5、SHA等,散列的对象:“密码+salt”,salt其实是干扰数据。

散列算法的5种实现类:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sAnpz2Jt-1640335050061)(images/image-20211222100842451.png)]
tips:其中6种加密实现类继承于

  1. SimpleHash

关键字:salt→

  1. SecureRandomNumberGenerator

​ password→

  1. SimpleHash

摘要算法工具类:用于对密码加密

  1. /*
  2. *@param1:算法名称
  3. *@param2:明文
  4. *@param3:盐值
  5. *@param4;加密次数
  6. public SimpleHash(String algorithmName,Object source,@Nullable Object salt,int hashIterations)
  7. */publicclassDigestsUtil{//算法类型publicstaticfinalString SHA1="SHA-1";//加密次数publicstaticfinalInteger ITERATION=512;/**sha1摘要算法
  8. * @param input 明文字符串
  9. * @param salt 干扰数据
  10. * @return
  11. */publicstaticStringsha1(String plaintext,String salt){returnnewSimpleHash(SHA1,plaintext,salt,ITERATION).toString();}/**
  12. * 随机生成salt
  13. * @return salt(16进制)
  14. */publicstaticStringcreateSalt(){SecureRandomNumberGenerator secureRandomNumberGenerator =newSecureRandomNumberGenerator();return secureRandomNumberGenerator.nextBytes().toHex();}/**
  15. *生成密码和salt的密文
  16. * @param pwd
  17. * @return
  18. */publicstaticMap<String,String>entryptPassword(String pwd){HashMap<String,String> map =newHashMap<>();String salt=createSalt();String password=sha1(pwd,salt);
  19. map.put("salt",salt);
  20. map.put("password",password);return map;}}

测试类:

  1. System.out.println(DigestsUtil.entryptPassword("123").toString());

运行结果:

  1. {password=54eeefc1368c375feeac0f71e1e6c929d4d5d6f1, salt=ba18ca250fd8442eaccfa3a03c3a5530}

4.3 认证

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qHNln0af-1640335050062)(images/image-20211216205515328-16396593438001.png)]

认证流程

【第一步】:Shiro把用户的数据封装成标识token,token一般封装着用户名,密码等信息

  1. UsernamePasswordToken

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9RpCEswd-1640335050062)(images/image-20211224154708894.png)]
【第二步】:使用Subject门面获取到封装着用户的数据的标识token

  1. subject.login(usernamePasswordToken);

【第三步】:Subject把标识token交给SecurityManager,SecurityManager再把标识token委托给认证器Authenticator进行身份验证。

​ 认证器的作用一般是用来指定如何验证,它规定本次认证用到哪些Realm

【第四步】:认证器Authenticator将传入的标识token,与数据源Realm对比,验证token是否合法

关键字:

  1. doGetAuthenticationInfo

  1. SimpleAuthenticationInfo

案例一:登陆测试1

数据来自shiro.ini文件

第一步:导入依赖

  1. <dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring-boot-starter</artifactId><version>1.8.0</version></dependency>

第二步:创建shiro.ini

  1. #声明用户账号
  2. [users]
  3. jay=1234

第三步:测试代码

  1. publicvoidshiroLogin(){//1.导入ini配置创建工厂IniSecurityManagerFactory factory =newIniSecurityManagerFactory("classpath:shiro.ini");//2.通过工厂构建安全构建器SecurityManager securityManager = factory.getInstance();//3.通过工具类让安全构建器生效SecurityUtils.setSecurityManager(securityManager);//4.通过工具类获取subject主体Subject subject =SecurityUtils.getSubject();//5.构建账号和密码UsernamePasswordToken usernamePasswordToken =newUsernamePasswordToken("jay","1234");//6.使用subject登录
  2. subject.login(usernamePasswordToken);//7.输出状态System.out.println("登陆状态:"+subject.isAuthenticated());}

截图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WYZYUlNs-1640335050063)(images/image-20211205212700417.png)]

案例二:登陆测试2

数据来自数据库

【第一步】:自定义realm

  1. publicclassDefinitionRealmextendsAuthorizingRealm{/*
  2. * 认证方法
  3. */@OverrideprotectedAuthenticationInfodoGetAuthenticationInfo(AuthenticationToken authenticationToken)throwsAuthenticationException{String loginName =(String)authenticationToken.getPrincipal();//模拟数据库查询SecurityServiceImpl securityService =newSecurityServiceImpl();String password = securityService.findPasswordByUserName(loginName);if(password==""){thrownewAuthenticationException("账号不存在");}returnnewSimpleAuthenticationInfo(loginName,password,getName());}/*
  4. *鉴权方法
  5. */@OverrideprotectedAuthorizationInfodoGetAuthorizationInfo(PrincipalCollection principalCollection){returnnull;}}

【第二步】:创建shiro.ini definitionRealm生效

  1. [main]
  2. definitionRealm=edu.pinkhub.shrio_demo.realm.DefinitionRealm
  3. securityManager.realms=$definitionRealm

【第三步】:测试

  1. @TestpublicvoidshiroLogin(){//1.导入ini配置创建工厂IniSecurityManagerFactory factory =newIniSecurityManagerFactory("classpath:shiro.ini");//2.工厂构建安全构建器SecurityManager securityManager = factory.getInstance();//3.通过工具类让安全构建器生效SecurityUtils.setSecurityManager(securityManager);//4.通过工具获取subject主体Subject subject =SecurityUtils.getSubject();//5.构建账号和密码UsernamePasswordToken usernamePasswordToken =newUsernamePasswordToken("jay","1234");//6.使用subject登录
  2. subject.login(usernamePasswordToken);//7.输出状态System.out.println("登陆状态:"+subject.isAuthenticated());}

案例三:登录测试3

Realm使用散列算法模拟登录

【第一步】:创建shiro.ini,使自定义realm生效

  1. [main]
  2. definitionRealm=edu.pinkhub.shrio_demo.realm.DefinitionRealm
  3. securityManager.realms=$definitionRealm

【第二步】:创建service接口

  1. publicinterfaceSecurityService{Map<String,String>findPasswordByUserName(String userName);}

【第三步】:创建service实现类,模拟根据用户名从数据库查询其加密密码、角色列表、权限列表

  1. @ServicepublicclassSecurityServiceImplimplementsSecurityService{@OverridepublicMap<String,String>findPasswordByUserName(String userName){returnDigestsUtil.entryptPassword("123456");}}

【第四步】:创建realm

  1. publicclassDefinitionRealmextendsAuthorizingRealm{publicDefinitionRealm(){//1.指定密码匹配方式HashedCredentialsMatcher hashedCredentialsMatcher =newHashedCredentialsMatcher(DigestsUtil.SHA1);//2.指定密码迭代次数
  2. hashedCredentialsMatcher.setHashIterations(DigestsUtil.ITERATION);//3.生效setCredentialsMatcher(hashedCredentialsMatcher);}/*
  3. * 认证方法
  4. * */@OverrideprotectedAuthenticationInfodoGetAuthenticationInfo(AuthenticationToken authenticationToken)throwsAuthenticationException{String loginName =(String)authenticationToken.getPrincipal();SecurityServiceImpl securityService =newSecurityServiceImpl();Map<String,String> map = securityService.findPasswordByUserName(loginName);if(map.isEmpty()){thrownewAuthenticationException("账号不存在");}String password=map.get("password");String salt = map.get("salt");returnnewSimpleAuthenticationInfo(loginName,password,ByteSource.Util.bytes(salt),getName());}/*
  5. 鉴权方法
  6. */@OverrideprotectedAuthorizationInfodoGetAuthorizationInfo(PrincipalCollection principalCollection){returnnull;}}

【第五步】:编写测试类

  1. @TestpublicvoidshiroLogin(){//1.导入ini配置创建工厂IniSecurityManagerFactory factory =newIniSecurityManagerFactory("classpath:shiro.ini");//2.工厂构建安全构建器SecurityManager securityManager = factory.getInstance();//3.通过工具类让安全构建器生效SecurityUtils.setSecurityManager(securityManager);//4.通过工具获取subject主体Subject subject =SecurityUtils.getSubject();//5.构建账号和密码UsernamePasswordToken usernamePasswordToken =newUsernamePasswordToken("pinkhub","123456");//6.使用subject登录
  2. subject.login(usernamePasswordToken);//7.输出状态System.out.println("登陆状态:"+subject.isAuthenticated());}

源码追踪:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eCZVxt1a-1640335050063)(images/image-20211218162908863.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YGcAcxx3-1640335050064)(images/image-20211218163004884.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f7gzO65N-1640335050064)(images/image-20211218163046291.png)]在这里插入图片描述
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yxBH5D10-1640335050065)(images/image-20211218163206170.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HkcdzBfd-1640335050065)(images/image-20211218163245071.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DFmPrbTZ-1640335050066)(images/image-20211218163321568.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TSH0k0ca-1640335050066)(images/image-20211218163435594.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vvz5U94Y-1640335050066)(images/image-20211218163818096.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i4HA0S0G-1640335050066)(images/image-20211218163934228.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8YVDrwMY-1640335050067)(images/image-20211224150622991.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7rsTRBU7-1640335050067)(images/image-20211218164232851.png)]
将token、info密码匹配结果boolean值,返回

  1. subject.isAuthenticated()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tUNGjjgx-1640335050067)(images/image-20211224155008578.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iK4QTX9A-1640335050067)(images/image-20211218172809319.png)]

4.4 授权

前提:用户必须通过认证;角色和权限存放在数据库中

基本流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jiBkMUOj-1640335050068)(images/ShiroAuthorizationSequence.png)]

【第一步】:首先调用

  1. Subject.isPermitted*/hasRole*

接口,然后委托给

  1. SecurityManager

(安全管理器)

【第二步】:

  1. SecurityManager

在委托给内部组件

  1. Authorizer

(授权器)

【第三步】:

  1. Authorizer

再将请求委托给

  1. Realm

去做

【第四步】:

  1. Realm

将用户请求的参数封装成权限对象,再从我们重写的

  1. doGetAuthorizationInfo

方法中获取从数据库中查询到的权限集合

【第五步】:

  1. Realm

将用户传入的权限对象,与从数据库查出的权限对象进行一一对比。如果用户传入的权限对象在数据库中查出来的权限对象中,则返回true,否则返回false

关键字:

  1. doGetAuthorizationInfo

  1. SimpleAuthorizationInfo

ShiroConfig配置

配置内容:

(1)创建自定义

  1. ShiroDbRealm

实现,用于权限认证、授权、加密方式的管理,同时从数据库中取得相关的角色、资源、用户的信息,然后交于DefaultWebSecurityManager权限管理器管理

(2)创建

  1. DefaultWebSecurityManager

权限管理器用于管理DefaultWebSessionManager会话管理器、ShiroDbRealm

(3)创建

  1. ShiroFilterFactoryBean

的shiro过滤器指定权限管理器、同时启动连接链及登录URL、未登录的URL的跳转

(4)创建

  1. SimpleCookie

,访问项目时,会在客户端中cookie中存放ShiroSession

(5)创建

  1. DefaultWebSessionManager

会话管理器定义cookie机制、定时刷新、全局会话超时时间然后交于DefaultWebSecurityManager权限管理器管理

  1. @ConfigurationpublicclassShiroConfig{/**
  2. * 创建ShiroDBRealm交给spring容器
  3. * @return
  4. */@Bean("shiroDBRealm")publicShiroDBRealmshiroDBRealm(){returnnewShiroDBRealm();}/**
  5. * 安全管理器
  6. * @param userRealm
  7. * @return
  8. */@Bean(name ="securityManager")publicDefaultWebSecurityManagerdefaultWebSecurityManager(@Qualifier("shiroDBRealm")ShiroDBRealm userRealm){DefaultWebSecurityManager securityManager =newDefaultWebSecurityManager();
  9. securityManager.setRealm(userRealm);return securityManager;}/**
  10. * 拦截器--拦截请求
  11. * 常用的过滤器:
  12. * anno:无需认证即可访问
  13. * authc:必须认证才能访问
  14. * perms:拥有对某个资源的权限才能访问
  15. * role:拥有某个角色权限才能访问
  16. * @param securityManager
  17. * @return
  18. */@BeanpublicShiroFilterFactoryBeanshiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){ShiroFilterFactoryBean shiroFilterFactoryBean =newShiroFilterFactoryBean();//拦截HashMap<String,String> filterMap =newHashMap<>();//filterMap.put("/user/login", "anon");
  19. filterMap.put("/user/add","perms[add]");
  20. filterMap.put("/user/edit","perms[update]");
  21. shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);//没通过验证,触发登录拦截请求,跳转登陆页面
  22. shiroFilterFactoryBean.setLoginUrl("/user/toLogin");//触发未认证请求,跳转未授权页面
  23. shiroFilterFactoryBean.setUnauthorizedUrl("/user/noAuth");
  24. shiroFilterFactoryBean.setSecurityManager(securityManager);return shiroFilterFactoryBean;}}

案例一:用户权限校验

  1. /*前提:认证成功
  2. (1)通过doGetAuthorizationInfo方法实现鉴权
  3. (2)使用Subject类来实现权限的校验
  4. *//**************************************************运行结果********************************************************************/
  5. 登陆状态:true
  6. 当前用户是否拥有管理员角色true
  7. 当前用户是否拥有查看订单的权限true
  8. 当前用户没有coder角色

【1】模拟从数据库查出的角色和权限列表

  1. @OverridepublicMap<String,String>findPasswordByUserName(String userName){returnDigestsUtil.entryptPassword("123456");}@OverridepublicList<String>findRoleByUserName(String userName){List<String> roleList =newArrayList();
  2. roleList.add("admin");
  3. roleList.add("dev");
  4. roleList.add("student");return roleList;}@OverridepublicList<String>findPermissionByUserName(StringUserName){List<String> list=newArrayList();
  5. list.add("order:add");
  6. list.add("order:del");
  7. list.add("order:update");
  8. list.add("order:query");return list;}

【2】编写

  1. doGetAuthorizationInfo
  1. publicclassDefinitionRealmextendsAuthorizingRealm{publicDefinitionRealm(){//1.指定密码匹配方式HashedCredentialsMatcher hashedCredentialsMatcher =newHashedCredentialsMatcher(DigestsUtil.SHA1);//2.指定密码迭代次数
  2. hashedCredentialsMatcher.setHashIterations(DigestsUtil.ITERATION);//3.生效setCredentialsMatcher(hashedCredentialsMatcher);}/*
  3. * 认证方法
  4. * */@OverrideprotectedAuthenticationInfodoGetAuthenticationInfo(AuthenticationToken authenticationToken)throwsAuthenticationException{String loginName =(String)authenticationToken.getPrincipal();System.out.println(loginName);SecurityServiceImpl securityService =newSecurityServiceImpl();Map<String,String> map = securityService.findPasswordByUserName(loginName);if(map.isEmpty()){thrownewAuthenticationException("账号不存在");}System.out.println(getName());String password=map.get("password");String salt = map.get("salt");returnnewSimpleAuthenticationInfo(loginName,password,ByteSource.Util.bytes(salt),getName());}/*
  5. 鉴权方法
  6. *principal:用户认证凭证信息,就是认证doGetAuthenticationInfo()方法的返回值的第一个参数
  7. */@OverrideprotectedAuthorizationInfodoGetAuthorizationInfo(PrincipalCollection principal){//用户凭证信息,当前账户名String primaryPrincipal =(String)principal.getPrimaryPrincipal();//从数据库中查询用户的角色和权限SecurityServiceImpl securityService =newSecurityServiceImpl();List<String> roles = securityService.findRoleByUserName(primaryPrincipal);List<String> permissions = securityService.findPermissionByUserName(primaryPrincipal);//构建资源校验对象SimpleAuthorizationInfo authorizationInfo =newSimpleAuthorizationInfo();
  8. authorizationInfo.addRoles(roles);
  9. authorizationInfo.addStringPermissions(permissions);return authorizationInfo;}}

【3】编写测试代码

  1. @TestpublicSubjectlogin(){//1.导入ini配置创建工厂IniSecurityManagerFactory factory =newIniSecurityManagerFactory("classpath:shiro.ini");//2.工厂构建安全构建器SecurityManager securityManager = factory.getInstance();//3.通过工具类让安全构建器生效SecurityUtils.setSecurityManager(securityManager);//4.通过工具获取subject主体Subject subject =SecurityUtils.getSubject();System.out.println(subject);//5.构建账号和密码UsernamePasswordToken usernamePasswordToken =newUsernamePasswordToken("pinkhub","123456");//6.使用subject登录
  2. subject.login(usernamePasswordToken);return subject;}@TestpublicvoidtestRole(){Subject subject =login();System.out.println("登陆状态:"+subject.isAuthenticated());System.out.println("当前用户是否拥有管理员角色"+subject.hasRole("admin"));System.out.println("当前用户是否拥有查看订单的权限"+subject.isPermitted("order:query"));try{
  3. subject.checkRole("coder");}catch(Exception e){System.out.println("当前用户没有coder角色");}}

【4】运行截图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ORjOeNM9-1640335050068)(images/image-20211218173305854.png)]

第五章 项目实战

登录、注册、用户权限管理

前提:准备user表;导入shiro依赖;导入

  1. DigestsUtil.java

  1. Result.java

  1. ResultCode.java

工具类

框架:sprintboot+mybatisPlus+shiro+thymeleaf

项目结构图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RzWxNUGk-1640335050068)(images/image-20211221154552375.png)]
【第0步】准备工作

(1)添加依赖

  1. <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--lombok--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!--mysql--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.49</version></dependency><!--mybatisPlus--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.4.3.3</version></dependency><!--druid,也可以使用shiro-spring1.4.0--><dependency><groupId>com.alibaba</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.8</version></dependency><!--shiro--><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring-boot-starter</artifactId><version>1.8.0</version></dependency><!--thymeleaf--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId><version>2.6.1</version></dependency><!--thymeleaf-extras-shiro--><dependency><groupId>com.github.theborakompanioni</groupId><artifactId>thymeleaf-extras-shiro</artifactId><version>2.1.0</version></dependency>

(2)编写配置文件

  1. yml
  1. server:port:8847spring:datasource:druid:username: root
  2. password:123456url: jdbc:mysql://localhost:3306/books
  3. #url: jdbc:mysql://localhost:3306/books?useUnicode=true&&characterEncoding=utf-8&&useSSL=false&serverTimezone=GMT%2b8mybatis-plus:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

【第一步】编写User实体类

  1. @Data@ToString@NoArgsConstructor@AllArgsConstructor@TableName("tb_user")publicclassUserimplementsSerializable{@TableId(value ="id",type =IdType.AUTO)publicInteger id;@TableField("account")publicString account;@TableField("real_name")publicString realName;@TableField("password")publicString password;@TableField("salt")publicString salt;@TableField("title")publicString title;@TableField("status")publicInteger status;@TableField("user_id")publicString userId;@TableField("perms")publicString perms;}

【第二步】编写mapper层

  1. @MapperpublicinterfaceUserMapperextendsBaseMapper<User>{}

【第三步】编写service层

  1. ---------------------------------------UserService.java-------------------------------------------------------------------------publicinterfaceUserServiceextendsIService<User>{}---------------------------------------UserServiceImpl.java---------------------------------------------------------------------@ServicepublicclassUserServiceImplextendsServiceImpl<UserMapper,User>implementsUserService{}

【第四步】编写controller

  1. Application.java
  1. @ControllerpublicclassAppplicationController{@RequestMapping({"/","/index"})publicStringindex(Model model){
  2. model.addAttribute("msg","hello,shiro");return"login";}}
  1. UserController.java
  1. @RequestMapping("/user/")@ControllerpublicclassUserControlller{@AutowiredpublicUserService userService;@RequestMapping("toRegister")publicStringtoRegister(){return"register";}@PostMapping("register")publicStringregister(HttpServletRequest request,Model model){String account = request.getParameter("account");String _password = request.getParameter("password");Map<String,String> info =DigestsUtil.entryptPassword(_password);String password = info.get("password");String salt = info.get("salt");QueryWrapper<User> wrapper =newQueryWrapper<>();
  2. wrapper.eq("account",account);User userDB = userService.getOne(wrapper);if(userDB!=null){
  3. model.addAttribute("msg","账号已存在");return"register";}else{User user =newUser();
  4. user.setAccount(account);
  5. user.setPassword(password);
  6. user.setSalt(salt);
  7. user.setRealName("pinkhub");
  8. user.setStatus(1);Random random =newRandom();
  9. user.setUserId(Integer.toString(random.nextInt(1000)));
  10. user.setTitle("普通用户");
  11. user.setPerms("query,update");
  12. userService.save(user);return"login";}}@PostMapping("login")publicStringlogin(String account,String password,Model model){UsernamePasswordToken token =newUsernamePasswordToken(account, password);Subject subject =SecurityUtils.getSubject();try{
  13. subject.login(token);return"index";}catch(UnknownAccountException e){
  14. model.addAttribute("msg","账号不正确");return"login";}catch(IncorrectCredentialsException e){
  15. model.addAttribute("msg","密码错误");return"login";}}@RequestMapping("add")publicStringadd(){return"/user/add";}@RequestMapping("edit")publicStringedit(){return"/user/edit";}@RequestMapping("noAuth")publicStringnoAuth(){return"noAuth";}@RequestMapping("toLogin")publicStringtoLogin(){return"login";}

【第五步】编写realm

  1. ShiroDBRealm.java
  1. publicclassShiroDBRealmextendsAuthorizingRealm{@AutowiredpublicUserService userService;publicShiroDBRealm(){//指定密码匹配方式HashedCredentialsMatcher matcher =newHashedCredentialsMatcher(DigestsUtil.SHA1);//指定密码迭代次数
  2. matcher.setHashIterations(DigestsUtil.ITERATION);//匹配生效setCredentialsMatcher(matcher);}/**
  3. * 认证
  4. * @param token
  5. * @return
  6. * @throws AuthenticationException
  7. */@OverrideprotectedAuthenticationInfodoGetAuthenticationInfo(AuthenticationToken token)throwsAuthenticationException{System.out.println("执行了doGetAuthenticationInfo()方法");UsernamePasswordToken tk=(UsernamePasswordToken) token;QueryWrapper<User> wrapper =newQueryWrapper<User>();
  8. wrapper.eq("account",tk.getUsername());User user = userService.getOne(wrapper);if(user==null){returnnull;}Subject subject =SecurityUtils.getSubject();Session session = subject.getSession();
  9. session.setAttribute("loginUser",user);returnnewSimpleAuthenticationInfo(user,user.getPassword(),ByteSource.Util.bytes(user.getSalt()),getName());}/**
  10. * 授权
  11. * @param principal为认证返回的第一个参数,user
  12. * @return
  13. */@OverrideprotectedAuthorizationInfodoGetAuthorizationInfo(PrincipalCollection principal){System.out.println("执行了doGetAuthorizationInfo()方法");SimpleAuthorizationInfo info =newSimpleAuthorizationInfo();Subject subject =SecurityUtils.getSubject();//认证成功后,返回信息的第一个参数User user =(User)subject.getPrincipal();System.out.println(user);List<String> permList =newArrayList<>();String[] perms = user.getPerms().split(",");for(String perm:perms){
  14. permList.add(perm);}
  15. info.addStringPermissions(permList);return info;}}

【第六步】编写config

  1. ShiroConfig.java
  1. @ConfigurationpublicclassShiroConfig{/**
  2. * 创建ShiroDBRealm交给spring容器
  3. * @return
  4. */@Bean("shiroDBRealm")publicShiroDBRealmshiroDBRealm(){returnnewShiroDBRealm();}/**
  5. * 安全管理器
  6. * @param userRealm
  7. * @return
  8. */@Bean(name ="securityManager")publicDefaultWebSecurityManagerdefaultWebSecurityManager(@Qualifier("shiroDBRealm")ShiroDBRealm userRealm){DefaultWebSecurityManager securityManager =newDefaultWebSecurityManager();
  9. securityManager.setRealm(userRealm);return securityManager;}/**
  10. * 拦截器--拦截请求
  11. * 常用的过滤器:
  12. * anno:无需认证即可访问
  13. * authc:必须认证才能访问
  14. * perms:拥有对某个资源的权限才能访问
  15. * role:拥有某个角色权限才能访问
  16. * @param securityManager
  17. * @return
  18. */@BeanpublicShiroFilterFactoryBeanshiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){ShiroFilterFactoryBean shiroFilterFactoryBean =newShiroFilterFactoryBean();//拦截HashMap<String,String> filterMap =newHashMap<>();//filterMap.put("/user/login", "anon");
  19. filterMap.put("/user/add","perms[add]");
  20. filterMap.put("/user/edit","perms[update]");
  21. shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);//没通过验证,触发登录拦截请求,跳转登陆页面
  22. shiroFilterFactoryBean.setLoginUrl("/user/toLogin");//触发未认证请求,跳转未授权页面
  23. shiroFilterFactoryBean.setUnauthorizedUrl("/user/noAuth");
  24. shiroFilterFactoryBean.setSecurityManager(securityManager);return shiroFilterFactoryBean;}//整合ShiroDialect:用于整合shiro thymeleaf@BeanpublicShiroDialectgetShiroDialect(){returnnewShiroDialect();}}

【第七步】编写前端页面

  1. index.html
  1. <!DOCTYPEhtml><htmllang="en"xmlns:th="http://www.w3.org/1999/xhtml"xmlns:shiro="http://www.w3.org/1999/xhtml"><head><metacharset="UTF-8"><title>shiro</title><style>#header{height: 75px;}</style></head><body><divid="header"><imgth:src="@{../apache-shiro-logo.png}"style="height: 100%"></div><h1>首页</h1><pth:text="${msg}"style="color: red"></p><hr><divth:if="${session.loginUser==null}"><ath:href="@{/user/toLogin}">登录</a></div><divshiro:hasPermission="add"><ath:href="@{/user/add}"style="text-decoration: none">添加用户</a><span>|</span></div><divshiro:hasPermission="update"><ath:href="@{/user/edit}"style="text-decoration: none">修改用户</a></div></body></html>
  1. login.html
  1. <!DOCTYPEhtml><htmllang="en"xmlns:th="http://www.w3.org/1999/xhtml"><head><metacharset="UTF-8"><title>shiro</title><style>#login_form{display: block;position: absolute;transform:translate(-50%, -61.8%);/*左右方向的位置为50%, 上下方向的位置为黄金分割比例0.618*/left: 50%;top: 50%;width: 450px;}#header{height: 75px;}</style></head><body><divid="header"><imgth:src="@{../apache-shiro-logo.png}"style="height: 100%"></div><divid="login"><h1>登陆页面</h1><divid="tips"><pth:text="${msg}"style="color: red"></p></div><hr><divid="login_form"><formaction="/user/login"method="post">
  2. 账号:<inputtype="text"name="account"/><br><br>
  3. 密码:<inputtype="password"name="password"/><br><br><ath:href="@{/user/toRegister}"style="text-decoration: none">无账号,去注册</a><br><br><inputtype="submit"value="提交"/></form></div></div></body></html>
  1. noAuth.html
  1. <!DOCTYPEhtml><htmllang="en"xmlns:th="http://www.w3.org/1999/xhtml"><head><metacharset="UTF-8"><title>shiro</title><style>#header{height: 75px;}</style></head><body><divid="header"><imgth:src="@{../apache-shiro-logo.png}"style="height: 100%"></div><p>该用户无权限</p></body></html>
  1. register.html
  1. <!DOCTYPEhtml><htmllang="en"xmlns:th="http://www.w3.org/1999/xhtml"><head><metacharset="UTF-8"><title>shiro</title><style>#register_form{display: block;position: absolute;transform:translate(-50%, -61.8%);/*左右方向的位置为50%, 上下方向的位置为黄金分割比例0.618*/left: 50%;top: 50%;width: 450px;}#header{height: 75px;}</style></head><body><divid="header"><imgth:src="@{../apache-shiro-logo.png}"style="height: 100%"></div><divid="register"><h1>注册页面</h1><divid="tips"><pth:text="${msg}"style="color: red"></p></div><hr><divid="register_form"><formth:action="@{/user/register}"method="post">
  2. 账号:<inputtype="text"name="account"/><br><br>
  3. 密码:<inputtype="password"name="password"/><br><br><inputtype="submit"/></form></div></div></body></html>
  1. add.html
  1. <!DOCTYPEhtml><htmllang="en"xmlns:th="http://www.w3.org/1999/xhtml"><head><metacharset="UTF-8"><title>shiro</title><style>#header{height: 75px;}</style></head><body><divid="header"><imgth:src="@{../apache-shiro-logo.png}"style="height: 100%"></div><h1>shiro权限控制</h1><ath:href="@{/user/toLogin}"style="text-decoration: none">退出</a><hr><pstyle="color: green">
  2. 增加用户
  3. </p></body></html>
  1. edit.html
  1. <!DOCTYPEhtml><htmllang="en"xmlns:th="http://www.w3.org/1999/xhtml"><head><metacharset="UTF-8"><title>shiro</title><style>#header{height: 75px;}</style></head><body><divid="header"><imgth:src="@{../apache-shiro-logo.png}"style="height: 100%"></div><h1>shiro权限控制</h1><ath:href="@{/user/toLogin}"style="text-decoration: none">退出</a><hr><pstyle="color: green">
  2. 修改用户
  3. </p></body></html>

项目截图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gv4xueah-1640335050069)(images/image-20211221164000722.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VXg2iyCa-1640335050069)(images/image-20211221164041138.png)]
账号:00

密码:000000 权限:update
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k1WZ0QIN-1640335050069)(images/image-20211221163857805.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-H26oMmpt-1640335050069)(images/image-20211221162946908.png)]


账号:qiang

密码:000000 权限:add
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-w8sF7wuA-1640335050069)(images/image-20211221163021623.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F8MFSEbj-1640335050070)(images/image-20211221163111644.png)]


ending.....


本文转载自: https://blog.csdn.net/weixin_44490884/article/details/122131259
版权归原作者 PinkHub 所有, 如有侵权,请联系我们删除。

“Apache shiro 权限框架”的评论:

还没有评论