Spring Boot学习之Shiro

浅浅的花香味﹌ 2024-03-27 09:31 153阅读 0赞

文章目录

  • 零 全部源码地址
  • 一 Shiro简介
    • 1.1 Shiro功能
    • 1.2 Shiro架构(外部视角)
    • 1.3 Shiro架构(内部视角)
  • 二 Shiro快速入门
    • 2.1 演示代码&部分源码解读
  • 三 Spring Boot集成Shio
    • 3.0 准备操作
    • 3.1 整合Shiro
    • 3.2 页面拦截实现
    • 3.3 登录认证
    • 3.4 整合数据库
    • 3.5 用户授权操作
    • 3.6 Shiro授权
    • 3.7 整合thymeleaf
    • 3.8 效果展示

零 全部源码地址

  • 全部源码

一 Shiro简介

  • Apache Shiro 是一个Java 的安全(权限)框架。
  • Shiro可以完成,认证,授权,加密,会话管理,Web集成,缓存等。
  • Shiro官网

1.1 Shiro功能

在这里插入图片描述


















































功能 说明
Authentication 身份认证、登录,验证用户是不是拥有相应的身份;
Authorization 授权,即权限验证,验证某个已认证的用户是否拥有某个权限,即判断用户能否进行某些操作,如:验证某个用户是否拥有某个角色,或者细粒度的验证某个用户对某个资源是否具有某个权限
Session Manager 会话管理,即用户登录后就是第一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通的JavaSE环境,也可以是Web环境;
Cryptography 加密,保护数据的安全性,如密码加密存储到数据库中,而不是明文存储;
Web Support Web支持,可以非常容易的集成到Web环境;
Caching 缓存,比如用户登录后,其用户信息,拥有的角色、权限不必每次去查,这样可以提高效率
Concurrency Shiro支持多线程应用的并发验证,即,如在一个线程中开启另一个线程,能把权限自动的传播过去
Testing 提供测试支持
Run As 允许一个用户假装为另一个用户(如果他们允许)的身份进行访问
Remember Me 记住登录功能,即一次登录后,下次再来的话不用登录

1.2 Shiro架构(外部视角)

  • 从应用程序角度来观察如何使用shiro完成工作
    在这里插入图片描述
  • subject:

    • 应用代码直接交互的对象是Subject【Shiro的对外API核心就是Subject】
    • 与当前应用交互的任何东西都是Subject,与Subject的所有交互都会委托给SecurityManager
    • Subject其实是一个门面,SecurityManageer 才是实际的执行者
  • SecurityManager

    • 安全管理器,即所有与安全有关的操作都会与SercurityManager交互,并且它管理着所有的Subject。
    • 它是Shiro的核心,它负责与Shiro的其他组件进行交互,它相当于SpringMVC的DispatcherServlet的角色
  • Realm

    • Shiro从Realm获取安全数据(如用户,角色,权限)
    • SecurityManager 要验证用户身份,需要从Realm 获取相应的用户进行比较,来确定用户的身份是否合法;也需要从Realm得到用户相应的角色、权限,进行验证用户的操作是否能够进行
    • 可以把Realm看成DataSource

1.3 Shiro架构(内部视角)

在这里插入图片描述










































组件名称 说明
Subject 任何可以与应用交互的 ‘用户’
Security Manager Shiro的心脏,所有具体的交互都通过Security Manager进行控制,它管理者所有的Subject,且负责进行认证,授权,会话,及缓存的管理。
Authenticator 负责Subject认证,是一个扩展点,可以自定义实现;可以使用认证策略(Authentication Strategy)
Authorizer 授权器访问控制器】用来决定主体是否有权限进行相应的操作【即控制着用户能访问应用中的那些功能】
Realm 可以有一个或者多个的realm,可以认为是安全实体数据源【即用于获取安全实体的,可以用JDBC实现,也可以是内存实现等等,由用户提供;所以一般在应用中都需要实现自己的realm】
SessionManager 管理Session生命周期的组件,而Shiro并不仅仅可以用在Web环境,也可以用在普通的JavaSE环境中
CacheManager 缓存控制器,来管理如用户,角色,权限等缓存的;因为这些数据基本上很少改变,放到缓存中后可以提高访问的性能;
Cryptography 密码模块,Shiro 提供了一些常见的加密组件用于密码加密,解密

二 Shiro快速入门

2.1 演示代码&部分源码解读

  • 官方10分钟快速入门
  1. 创建一个maven工程删掉不必要的东西
    在这里插入图片描述
  2. 根据官方文档,我们来导入Shiro的依赖

    1. <dependencies>
    2. <dependency>
    3. <groupId>org.apache.shiro</groupId>
    4. <artifactId>shiro-core</artifactId>
    5. <version>1.4.1</version>
    6. </dependency>
    7. <!-- configure logging -->
    8. <dependency>
    9. <groupId>org.slf4j</groupId>
    10. <artifactId>jcl-over-slf4j</artifactId>
    11. <version>1.7.21</version>
    12. </dependency>
    13. <dependency>
    14. <groupId>org.slf4j</groupId>
    15. <artifactId>slf4j-log4j12</artifactId>
    16. <version>1.7.21</version>
    17. </dependency>
    18. <dependency>
    19. <groupId>log4j</groupId>
    20. <artifactId>log4j</artifactId>
    21. <version>1.2.17</version>
    22. </dependency>
    23. </dependencies>
  3. 编写Shiro配置

    • log4j.properties

      log4j.rootLogger=INFO, stdout

      log4j.appender.stdout=org.apache.log4j.ConsoleAppender
      log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
      log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n

      General Apache libraries

      log4j.logger.org.apache=WARN

      Spring

      log4j.logger.org.springframework=WARN

      Default Shiro logging

      log4j.logger.org.apache.shiro=INFO

      Disable verbose logging

      log4j.logger.org.apache.shiro.util.ThreadContext=WARN
      log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN

    • shiro.ini

      [users]

      user ‘root’ with password ‘secret’ and the ‘admin’ role

      root = secret, admin

      user ‘guest’ with the password ‘guest’ and the ‘guest’ role

      guest = guest, guest

      user ‘presidentskroob’ with password ‘12345’ (“That’s the same combination on

      my luggage!!!” ;)), and role ‘president’

      presidentskroob = 12345, president

      user ‘darkhelmet’ with password ‘ludicrousspeed’ and roles ‘darklord’ and ‘schwartz’

      darkhelmet = ludicrousspeed, darklord, schwartz

      user ‘lonestarr’ with password ‘vespa’ and roles ‘goodguy’ and ‘schwartz’

      lonestarr = vespa, goodguy, schwartz

      ——————————————————————————————————————-

      Roles with assigned permissions

      #

      Each line conforms to the format defined in the

      org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc

      ——————————————————————————————————————-

      [roles]

      ‘admin’ role has all permissions, indicated by the wildcard ‘*’

      admin = *

      The ‘schwartz’ role can do anything (*) with any lightsaber:

      schwartz = lightsaber:*

      The ‘goodguy’ role is allowed to ‘drive’ (action) the winnebago (type) with

      license plate ‘eagle5’ (instance specific id)

      goodguy = winnebago:drive:eagle5

  4. 编写QuickStrat

    1. import org.apache.shiro.SecurityUtils;
    2. import org.apache.shiro.authc.*;
    3. import org.apache.shiro.config.IniSecurityManagerFactory;
    4. import org.apache.shiro.mgt.SecurityManager;
    5. import org.apache.shiro.session.Session;
    6. import org.apache.shiro.subject.Subject;
    7. import org.apache.shiro.util.Factory;
    8. import org.slf4j.Logger;
    9. import org.slf4j.LoggerFactory;
  1. /**
  2. * Simple Quickstart application showing how to use Shiro's API.
  3. * @since 0.9 RC2
  4. */
  5. public class Quickstart {
  6. private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);
  7. public static void main(String[] args) {
  8. Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
  9. SecurityManager securityManager = factory.getInstance();
  10. //设置单例模式
  11. SecurityUtils.setSecurityManager(securityManager);
  12. /*
  13. 以下是核心代码
  14. */
  15. // 获取当前的对象 subject
  16. Subject currentUser = SecurityUtils.getSubject();
  17. // 通过当前对象拿到 session
  18. Session session = currentUser.getSession();
  19. session.setAttribute("someKey", "aValue");
  20. String value = (String) session.getAttribute("someKey");
  21. if (value.equals("aValue")) {
  22. log.info("Retrieved the correct value! [" + value + "]");
  23. }
  24. // 判断当前的用户是否被认证
  25. if (!currentUser.isAuthenticated()) {
  26. //token令牌 没有获取,随机
  27. UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
  28. token.setRememberMe(true);//设置记住功能
  29. try {
  30. currentUser.login(token);//执行登录操作~
  31. } catch (UnknownAccountException uae) {
  32. //用户名不存在
  33. log.info("There is no user with username of " + token.getPrincipal());
  34. } catch (IncorrectCredentialsException ice) {
  35. //密码错误
  36. log.info("Password for account " + token.getPrincipal() + " was incorrect!");
  37. } catch (LockedAccountException lae) {
  38. //
  39. log.info("The account for username " + token.getPrincipal() + " is locked. " +
  40. "Please contact your administrator to unlock it.");
  41. }
  42. // ... catch more exceptions here (maybe custom ones specific to your application?
  43. catch (AuthenticationException ae) {
  44. //unexpected condition? error?
  45. }
  46. }
  47. //say who they are:
  48. //print their identifying principal (in this case, a username):
  49. //获取当前用户的认证码——存取信息
  50. log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
  51. //test a role:检测角色
  52. if (currentUser.hasRole("schwartz")) {
  53. log.info("May the Schwartz be with you!");
  54. } else {
  55. log.info("Hello, mere mortal.");
  56. }
  57. //粗粒度
  58. //test a typed permission (not instance-level):检测权限
  59. if (currentUser.isPermitted("lightsaber:wield")) {
  60. log.info("You may use a lightsaber ring. Use it wisely.");
  61. } else {
  62. log.info("Sorry, lightsaber rings are for schwartz masters only.");
  63. }
  64. //细粒度
  65. //a (very powerful) Instance Level permission:是否拥有更高级的权限
  66. if (currentUser.isPermitted("winnebago:drive:eagle5")) {
  67. log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " +
  68. "Here are the keys - have fun!");
  69. } else {
  70. log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
  71. }
  72. //all done - log out!退出
  73. currentUser.logout();
  74. // 结束
  75. System.exit(0);
  76. }
  77. }
  1. 测试结果

    1. 2023-01-27 17:35:00,334 INFO [org.apache.shiro.session.mgt.AbstractValidatingSessionManager] - Enabling session validation scheduler...
    2. 2023-01-27 17:35:00,712 INFO [Quickstart] - Retrieved the correct value! [aValue]
    3. 2023-01-27 17:35:00,713 INFO [Quickstart] - User [lonestarr] logged in successfully.
    4. 2023-01-27 17:35:00,713 INFO [Quickstart] - May the Schwartz be with you!
    5. 2023-01-27 17:35:00,713 INFO [Quickstart] - You may use a lightsaber ring. Use it wisely.
    6. 2023-01-27 17:35:00,714 INFO [Quickstart] - You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. Here are the keys - have fun!
    7. 进程已结束,退出代码0

三 Spring Boot集成Shio

3.0 准备操作

  1. 搭建一个SpringBoot项目、选中web模块 在这里插入图片描述
  2. 导入Maven依赖 thymeleaf

    1. <!--thymeleaf模板-->
    2. <dependency>
    3. <groupId>org.thymeleaf</groupId>
    4. <artifactId>thymeleaf-spring5</artifactId>
    5. </dependency>
    6. <dependency>
    7. <groupId>org.thymeleaf.extras</groupId>
    8. <artifactId>thymeleaf-extras-java8time</artifactId>
    9. </dependency>
  3. 编写一个页面 index.html
    在这里插入图片描述

    1. <!DOCTYPE html>
    2. <html lang="en"xmlns:th="http://www.thymeleaf.org">
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>Title</title>
    6. </head>
    7. <body>
    8. <h1>首页</h1>
    9. <p th:text="${msg}"></p>
    10. </body>
    11. </html>
  4. 编写controller进行访问测试

    1. import org.springframework.stereotype.Controller;
    2. import org.springframework.ui.Model;
    3. import org.springframework.web.bind.annotation.RequestMapping;
    4. @Controller
    5. public class MyController {
    6. @RequestMapping({
    7. "/","/index"})
    8. public String toIndex(Model model){
    9. model.addAttribute("msg","hello,Shiro");
    10. return "index";
    11. }
    12. }

3.1 整合Shiro

  1. 导入Shiro 和 spring整合的依赖

    1. <!--
    2. subject -用户
    3. SecurityManager - 管理所有用户
    4. realm -连接数据
    5. -->
    6. <!--shiro-spring-->
    7. <!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
    8. <dependency>
    9. <groupId>org.apache.shiro</groupId>
    10. <artifactId>shiro-spring</artifactId>
    11. <version>1.9.1</version>
    12. </dependency>
  2. 编写Shiro 配置类 【config包下】

    1. import org.springframework.context.annotation.Configuration;
    2. //声明为配置类
    3. @Configuration
    4. public class ShiroConfig {
    5. //创建 ShiroFilterFactoryBean
    6. //创建 DefaultWebSecurityManager
    7. //创建 realm 对象
    8. }
  3. 先创建一个 realm 对象

    • 需要自定义一个 realm 的类,用来编写一些查询的方法,或者认证与授权的逻辑

      import org.apache.shiro.authc.AuthenticationException;
      import org.apache.shiro.authc.AuthenticationInfo;
      import org.apache.shiro.authc.AuthenticationToken;
      import org.apache.shiro.authz.AuthorizationInfo;
      import org.apache.shiro.realm.AuthorizingRealm;
      import org.apache.shiro.subject.PrincipalCollection;
      //自定义Realm
      public class UserRealm extends AuthorizingRealm {

      1. //执行授权逻辑
      2. @Override
      3. protected AuthorizationInfo
      4. doGetAuthorizationInfo(PrincipalCollection principals) {
      5. System.out.println("执行了=>授权逻辑PrincipalCollection");
      6. return null;
      7. }
      8. //执行认证逻辑
      9. @Override
      10. protected AuthenticationInfo
      11. doGetAuthenticationInfo(AuthenticationToken token) throws
      12. AuthenticationException {
      13. System.out.println("执行了=>认证逻辑AuthenticationToken");
      14. return null;
      15. }

      }

  4. 将这个类注册到Bean中【ShiroConfig中】

    1. @Configuration
    2. public class ShiroConfig {
    3. //创建 ShiroFilterFactoryBean
    4. //创建 DefaultWebSecurityManager
    5. //创建 realm 对象
    6. @Bean
    7. public UserRealm userRealm(){
    8. return new UserRealm();
    9. }
    10. }
  5. 创建 DefaultWebSecurityManager

    1. //创建 ShiroFilterFactoryBean
    2. @Bean
    3. public ShiroFilterFactoryBean
    4. getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){
    5. ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    6. //设置安全管理器
    7. shiroFilterFactoryBean.setSecurityManager(securityManager);
    8. return shiroFilterFactoryBean;
    9. }
  6. ShiroConfig全部代码

    1. //声明为配置类
    2. @Configuration
    3. public class ShiroConfig {
    4. //创建 ShiroFilterFactoryBean
    5. @Bean
    6. public ShiroFilterFactoryBean
    7. getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){
    8. ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    9. //设置安全管理器
    10. shiroFilterFactoryBean.setSecurityManager(securityManager);
    11. return shiroFilterFactoryBean;
    12. }
    13. //创建 DefaultWebSecurityManager
    14. @Bean(name = "securityManager")
    15. public DefaultWebSecurityManager
    16. getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){
    17. DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    18. //关联Realm
    19. securityManager.setRealm(userRealm);
    20. return securityManager;
    21. }
    22. //创建 realm 对象
    23. @Bean
    24. public UserRealm userRealm(){
    25. return new UserRealm();
    26. }
    27. }

3.2 页面拦截实现

  1. 编写两个页面、在templates目录下新建一个 user 目录 add.html update.html

    1. <body>
    2. <h1>add</h1>
    3. </body>
    4. <body>
    5. <h1>update</h1>
    6. </body>
  2. 编写跳转到页面的controller

    1. @RequestMapping("/user/add")
    2. public String toAdd(){
    3. return "user/add";
    4. }
    5. @RequestMapping("/user/update")
    6. public String toUpdate(){
    7. return "user/update";
    8. }
  3. 在index页面上,增加跳转链接

    1. <a th:href="@{/user/add}">add</a>
    2. <hr/>
    3. <a th:href="@{/user/update}">update</a>
  4. 添加Shiro的内置过滤器

    1. @Bean
    2. public ShiroFilterFactoryBean
    3. getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager){
    4. ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    5. //设置安全管理器
    6. shiroFilterFactoryBean.setSecurityManager(securityManager);
    7. /*
    8. 添加Shiro内置过滤器,常用的有如下过滤器:
    9. anon: 无需认证就可以访问
    10. authc: 必须认证才可以访问
    11. user: 如果使用了记住我功能就可以直接访问
    12. perms: 拥有某个资源权限才可以访问
    13. role: 拥有某个角色权限才可以访问
    14. */
    15. Map<String,String> filterMap = new LinkedHashMap<String, String>();
    16. filterMap.put("/user/add","authc");
    17. filterMap.put("/user/update","authc");
    18. shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
    19. return shiroFilterFactoryBean;
    20. }
  5. 编写自定义Login页面

    1. <!DOCTYPE html>
    2. <html lang="en">
    3. <head>
    4. <meta charset="UTF-8">
    5. <title>login</title>
    6. </head>
    7. <body>
    8. <p th:text="${msg}" style="color: red"></p>
    9. <form th:action="@{/login}">
    10. <p>用户名:<input type="text" name="username"></p>
    11. <p>密码:<input type="text" name="password"></p>
    12. <p>
    13. <button type="submit">登录</button>
    14. </p>
    15. </form>
    16. </body>
    17. </html>
  6. 编写跳转的controller

    1. @RequestMapping("/toLogin")
    2. public String toLogin(){
    3. return "login";
    4. }
  7. 在shiro中配置

    1. //shiroFilterFactoryBean
    2. @Bean
    3. public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager)
    4. {
    5. ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
    6. //设置安全管理器
    7. bean.setSecurityManager(defaultWebSecurityManager);
    8. //添加shiro的内置过滤器
    9. /*
    10. anon:无需认证既可以访问
    11. author:必须认证了才能访问
    12. user:必须拥有 记住我 功能才能用
    13. perms:拥有对某个资源的权限才能访问
    14. role:拥有某个角色权限才能访问
    15. //filterMap.put("/user/add","authc");
    16. // filterMap.put("/user/update","authc");
    17. */
    18. filterMap.put("/user/*","authc");
    19. bean.setFilterChainDefinitionMap(filterMap);
    20. //设置登录的页面
    21. bean.setLoginUrl("/toLogin");
    22. return bean;
    23. }

3.3 登录认证

  1. 编写一个登录的controller

    1. //登录操作
    2. @RequestMapping("/login")
    3. public String login(String username,String password,Model model){
    4. //使用shiro,编写认证操作
    5. //1. 获取Subject
    6. Subject subject = SecurityUtils.getSubject();
    7. //2. 封装用户的数据
    8. UsernamePasswordToken token = new UsernamePasswordToken(username,password);
    9. //3. 执行登录的方法,只要没有异常就代表登录成功!
    10. try {
    11. subject.login(token); //登录成功!返回首页
    12. return "index";
    13. } catch (UnknownAccountException e) {
    14. //用户名不存在
    15. model.addAttribute("msg","用户名不存在");
    16. return "login";
    17. } catch (IncorrectCredentialsException e) {
    18. //密码错误
    19. model.addAttribute("msg","密码错误");
    20. return "login";
    21. }
    22. }
  2. 在前端修改对应的信息输出或者请求

    • 登录页面增加一个 msg

    • 给表单增加一个提交地址


      用户名:


      密码:



  3. 在 UserRealm 中编写用户认证的判断逻辑

    1. //执行认证逻辑
    2. @Override
    3. protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationTokentoken) throws AuthenticationException {
    4. System.out.println("执行了=>认证逻辑AuthenticationToken");
    5. //假设数据库的用户名和密码
    6. String name = "root";
    7. String password = "123456";
    8. //1.判断用户名
    9. UsernamePasswordToken userToken = (UsernamePasswordToken)token;
    10. if (!userToken.getUsername().equals(name)){
    11. //用户名不存在
    12. return null; //shiro底层就会抛出 UnknownAccountException
    13. }
    14. //2. 验证密码,我们可以使用一个AuthenticationInfo实现类
    15. SimpleAuthenticationInfo
    16. // shiro会自动帮我们验证!重点是第二个参数就是要验证的密码!
    17. return new SimpleAuthenticationInfo("", password, "");
    18. }

3.4 整合数据库

  1. 导入Mybatis相关依赖

    1. <!--mysql-->
    2. <dependency>
    3. <groupId>mysql</groupId>
    4. <artifactId>mysql-connector-java</artifactId>
    5. </dependency>
    6. <dependency>
    7. <groupId>log4j</groupId>
    8. <artifactId>log4j</artifactId>
    9. <version>1.2.17</version>
    10. </dependency>
    11. <!-- druid -->
    12. <dependency>
    13. <groupId>com.alibaba</groupId>
    14. <artifactId>druid</artifactId>
    15. <version>1.2.13-SNSAPSHOT</version>
    16. </dependency>
  2. 编写配置文件-连接配置 application.yml

    1. spring:
    2. datasource:
    3. username: root
    4. password: xxxx
    5. url: jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    6. driver-class-name: com.mysql.cj.jdbc.Driver
    7. type: com.alibaba.druid.pool.DruidDataSource
  1. #Spring Boot 默认是不注入这些属性值的,需要自己绑定
  2. #druid 数据源专有配置
  3. initialSize: 5
  4. minIdle: 5
  5. maxActive: 20
  6. maxWait: 60000
  7. timeBetweenEvictionRunsMillis: 60000
  8. minEvictableIdleTimeMillis: 300000
  9. validationQuery: SELECT 1 FROM DUAL
  10. testWhileIdle: true
  11. testOnBorrow: false
  12. testOnReturn: false
  13. poolPreparedStatements: true
  14. #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
  15. #如果允许时报错 java.lang.ClassNotFoundException:org.apache.log4j.Priority
  16. #则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
  17. filters: stat,wall,log4j
  18. maxPoolPreparedStatementPerConnectionSize: 20
  19. useGlobalDataSourceStat: true
  20. connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
  1. 编写mybatis的配置

    1. #别名配置
    2. mybatis.type-aliases-package=com.yang.pojo
    3. mybatis.mapper-locations=classpath:mapper/*.xml
  2. 编写实体类,引入Lombok

    1. <dependency>
    2. <groupId>org.projectlombok</groupId>
    3. <artifactId>lombok</artifactId>
    4. </dependency>
    5. import lombok.AllArgsConstructor;
    6. import lombok.Data;
    7. import lombok.NoArgsConstructor;
    8. import org.apache.ibatis.type.Alias;
    9. /**
    10. * @author 缘友一世
    11. * date 2022/9/17-9:43
    12. */
    13. @Data
    14. @NoArgsConstructor
    15. @AllArgsConstructor
    16. @Alias("User")
    17. public class User {
    18. private int id;
    19. private String name;
    20. private String pwd;
    21. private String perms;
    22. }
  3. 编写Mapper接口

    1. import com.yang.pojo.User;
    2. import org.apache.ibatis.annotations.Mapper;
    3. import org.springframework.stereotype.Repository;
    4. /**
    5. * @author 缘友一世
    6. * date 2022/9/17-9:44
    7. */
    8. @Repository
    9. @Mapper
    10. public interface UserMapper {
    11. public User queryUserByName(String name);
    12. }
  4. 编写Mapper配置文件

    1. <?xml version="1.0" encoding="UTF-8" ?>
    2. <!DOCTYPE mapper
    3. PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    4. "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    5. <mapper namespace="com.yang.mapper.UserMapper">
    6. <select id="queryUserByName" parameterType="String"
    7. resultType="User">
    8. select * from user where name = #{name}
    9. </select>
    10. </mapper>
  5. 编写UserService 层

    1. import com.yang.pojo.User;
    2. /**
    3. * @author 缘友一世
    4. * date 2022/9/17-9:49
    5. */
    6. public interface UserService {
    7. public User queryUserByName(String name);
    8. }
    9. import com.yang.mapper.UserMapper;
    10. import com.yang.pojo.User;
    11. import org.springframework.beans.factory.annotation.Autowired;
    12. import org.springframework.stereotype.Service;
    13. /**
    14. * @author 缘友一世
    15. * date 2022/9/17-9:51
    16. */
    17. @Service
    18. public class UserServiceImpl implements UserService{
  1. @Autowired
  2. UserMapper userMapper;
  3. @Override
  4. public User queryUserByName(String name) {
  5. return userMapper.queryUserByName(name);
  6. }
  7. }
  1. 测试

    1. import com.yang.service.UserService;
    2. import com.yang.service.UserServiceImpl;
    3. import org.junit.jupiter.api.Test;
    4. import org.springframework.beans.factory.annotation.Autowired;
    5. import org.springframework.boot.test.context.SpringBootTest;
    6. @SpringBootTest
    7. class ShiroSpringbootApplicationTests {
    8. @Autowired
    9. UserServiceImpl userService;
    10. @Test
    11. void contextLoads() {
    12. System.out.println(userService.queryUserByName("小明"));
    13. }
    14. }

    在这里插入图片描述

  2. 改造UserRealm

    • 连接到数据库进行真实的操作

      import com.yang.pojo.User;
      import com.yang.service.UserService;
      import org.apache.shiro.SecurityUtils;
      import org.apache.shiro.authc.*;
      import org.apache.shiro.authz.AuthorizationInfo;
      import org.apache.shiro.authz.SimpleAuthorizationInfo;
      import org.apache.shiro.realm.AuthorizingRealm;
      import org.apache.shiro.session.Session;
      import org.apache.shiro.subject.PrincipalCollection;
      import org.apache.shiro.subject.Subject;
      import org.springframework.beans.factory.annotation.Autowired;

      /**

      • @author 缘友一世
      • date 2022/9/16-21:08
        */
        //自定义UserRealm
        public class UserRealm extends AuthorizingRealm {

        @Autowired UserService userService;
        //授权
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        1. System.out.println("执行了=>授权doGetAuthorizationInfo");
        2. //给资源进行授权
        3. SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        4. //添加资源的授权字符串
        5. //info.addStringPermission("user:add");//硬编码
        6. //拿到当前登录的这个对象
        7. Subject subject = SecurityUtils.getSubject();
        8. User currentPrincipal = (User)subject.getPrincipal();//拿到user对象
        9. //设置当前用户的权限,从数据库中查询而来
        10. info.addStringPermission(currentPrincipal.getPerms());
        11. return info;

        }
        //认证
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

        1. System.out.println("执行了=>认证doGetAuthorizationInfo");
        2. /*用户名 密码 数据获取
        3. String name="root";
        4. String password="123456";
        5. UsernamePasswordToken userToken=(UsernamePasswordToken) token;
        6. if(!userToken.getUsername().equals(name)) {
        7. return null;//抛出异常 UnknownAccountException
        8. }
        9. //密码认证,shiro做
        10. return new SimpleAuthenticationInfo("",password,"");
        11. */
        12. UsernamePasswordToken userToken=(UsernamePasswordToken) token;
        13. //连接真实数据库
        14. User user = userService.queryUserByName(userToken.getUsername());
        15. if(user==null) {
        16. return null;//抛出异常 UnknownAccountException
        17. }
        18. //为了完美,我们在用户登录后应该把信息放到Session中,我们完善下!在执行认证逻辑时候,加入session
        19. Subject currentSubject = SecurityUtils.getSubject();
        20. Session session = currentSubject.getSession();
        21. session.setAttribute("loginUser",user);
        22. //第一个参数类型principal 当事人;首要的;最主要的 将user对象传递给上面的授权操作
        23. return new SimpleAuthenticationInfo(user,user.getPwd(),"");

        }
        }

3.5 用户授权操作

  • 使用shiro的过滤器来拦截请求
  1. 在 ShiroFilterFactoryBean 中添加一个过滤器

    1. //授权过滤器
    2. filterMap.put("/user/add","perms[user:add]"); //大家记得注意顺序!
  • 当我们实现权限拦截后,shiro会自动跳转到未授权的页面
  1. 配置一个未授权的提示的页面,增加一个controller提示

    1. @RequestMapping("/noauth")
    2. @ResponseBody
    3. public String noAuth(){
    4. return "未经授权不能访问此页面";
    5. }
  2. 在shiroFilterFactoryBean 中配置一个未授权的请求页面

    1. shiroFilterFactoryBean.setUnauthorizedUrl("/noauth");

3.6 Shiro授权

  • 在UserRealm 中添加授权的逻辑,增加授权的字符串

    1. //授权
    2. @Override
    3. protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    4. System.out.println("执行了=>授权doGetAuthorizationInfo");
    5. //给资源进行授权
    6. SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    7. //添加资源的授权字符串
    8. //info.addStringPermission("user:add");//硬编码
    9. //拿到当前登录的这个对象
    10. Subject subject = SecurityUtils.getSubject();
    11. User currentPrincipal = (User)subject.getPrincipal();//拿到user对象
    12. //设置当前用户的权限,从数据库中查询而来
    13. info.addStringPermission(currentPrincipal.getPerms());
    14. return info;
    15. }
  • 在过滤器中,将 update 请求也进行权限拦截下

    1. //拦截
    2. LinkedHashMap<String, String> filterMap = new LinkedHashMap<>();
    3. //授权 正常情况下,没有授权会跳转到未授权页面
    4. filterMap.put("/user/add","perms[user:add]");
    5. filterMap.put("/user/update","perms[user:update]");

3.7 整合thymeleaf

  1. 添加maven依赖

    1. <!--shiro-thymeleaf-->
    2. <dependency>
    3. <groupId>com.github.theborakompanioni</groupId>
    4. <artifactId>thymeleaf-extras-shiro</artifactId>
    5. <version>2.1.0</version>
    6. </dependency>
  2. 配置一个shiro的Dialect ,在shiro的配置中增加一个Bean

    1. //配置ShiroDialect:方言,用于整合thymeleaf和shiro
    2. // 用于 thymeleaf 和 shiro 标签配合使用
    3. @Bean
    4. public ShiroDialect getShiroDialect(){
    5. return new ShiroDialect();
    6. }
  3. 修改前端的配置

    1. <div shiro:hasPermission="user:add">
    2. <a th:href="@{/user/add}">add</a>
    3. </div>
    4. <div shiro:hasPermission="user:update">
    5. <a th:href="@{/user/update}">update</a>
    6. </div>
  4. 在执行认证逻辑时候,加入session

    1. //为了完美,在用户登录后应该把信息放到Session中,我们完善下!在执行认证逻辑时候,加入session
    2. Subject currentSubject = SecurityUtils.getSubject();
    3. Session session = currentSubject.getSession();
    4. session.setAttribute("loginUser",user);
  5. 前端从session中获取,然后用来判断是否显示登录

    1. <p th:if="${session.loginUser==null}">
    2. <a th:href="@{/toLogin}">登录</a>
    3. </p>

3.8 效果展示

在这里插入图片描述
在这里插入图片描述

发表评论

表情:
评论列表 (有 0 条评论,153人围观)

还没有评论,来说两句吧...

相关阅读