• SpringBoot集成Shiro


    简介

    • Apache shiro 是Java的安全(权限)框架。
    • Shiro可以可以非常容易的开发出足够好的应用,其不仅可以用在JavaSE环境,也可以用在JAVAEE环境。
    • Shiro可以完成:认证授权、加密、会话管理,与web集成,缓存等。

    功能简介

    在这里插入图片描述

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

    Shiro架构

    在这里插入图片描述

    • Subject :应用代码直接交互的对象是Subject ,也就是说Shiro的对外API核心就是Subject。Subject 代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject ,如网络爬虫,机器人等;与Subject的所有交互都会委托给SecurityManager ;Subject其实是- -个门面, SecurityManager才是实际的执行者;

    • SecurityManager :安全管理器;即所有与安全有关的操作都会与SecurityManager交互;且其管理着所有Subject ;可以看出它是Shiro的核心,它负责与Shiro的其他组件进行交互,它相当于SpringMVC中
      DispatcherServlet的角色

    • SecurityManager :安全管理器;即所有与安全有关的操作都会与SecurityManager交互;且其管理着所有Subject ;可以看出它是Shiro的核心,它负责与Shiro的其他组件进行交互,它相当于SpringMVC中
      DispatcherServlet的角色

      在这里插入图片描述

    • Subject :任何可以与应用交互的“用户”;
      SecurityManager :相当于SpringMVC中的DispatcherServlet ;是Shiro的心脏;
      所有具体的交互都通过SecurityManager进行控制;它管理着所有Subject、负责进
      行认证、授权、会话及缓存的管理。

    • Authenticator :负责Subject认证,是一个扩展点,可以自定义实现;可以使用认证
      策略( Authentication Strategy ) , 即什么情况下算用户认证通过了;

    • Authorizer :授权器、即访问控制器,用来决定主体是否有权限进行相应的操作;即控
      制着用户能访问应用中的哪些功能;

    • Realm:可以有1个或多个Realm ,可以认为是安全实体数据源,即用于获取安全实体
      的;可以是JDBC实现,也可以是内存实现等等;由用户提供;所以一般在应用中都需要
      实现自己的Realm ;

    • SessionManager :管理Session生命周期的组件;而Shiro并不仅仅可以用在Web
      环境,也可以用在如普通的JavaSE环境

    • CacheManager :缓存控制器,来管理如用户、角色、权限等的缓存的;因为这些数据
      基本上很少改变,放到缓存中后可以提高访问的性能

    • Cryptography :密码模块, Shiro 提高了- -些常见的加密组件用于如密码加密/解密。

    SpringBoot集成

    SpringBoot整合Shiro环境搭建

       <dependencies>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-thymeleafartifactId>
            dependency>
    
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-testartifactId>
                <scope>testscope>
            dependency>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-webartifactId>
                <version>RELEASEversion>
                <scope>compilescope>
            dependency>
            <dependency>
                <groupId>org.apache.shirogroupId>
                <artifactId>shiro-spring-boot-web-starterartifactId>
                <version>1.7.1version>
            dependency>
            <dependency>
                <groupId>org.mortbay.jettygroupId>
                <artifactId>jettyartifactId>
                <version>6.1.25version>
            dependency>
        dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    测试环境

    新建一个controller页面

    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller
    public class MyController {
        @RequestMapping({"/","index"})
        public String toIndex(Model model){
            model.addAttribute("msg","hello,Shiro");
            return "index";
        }
        @RequestMapping("/user/add")
        public String add(){
            return "user/add";
        }
        @RequestMapping("/user/update")
        public String update(){
            return "user/update";
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    新建index.html页面

    DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Titletitle>
    head>
    <body>
        <h1>首页h1>
        <p th:text="${msg}">p>
    
        <hr>
    
        <a th:href="@{/user/add}">adda>
        <a th:href="@{/user/update}">updatea>
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    新建一个add.html页面

    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Titletitle>
    head>
    <body>
    <h1>addh1>
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    新建一个update.html页面

    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Titletitle>
    head>
    <body>
    <h1>updateh1>
    body>
    html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    编写导入配置类

    编写一个自定义类UserRealm

    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;
    
    public class UserRealm  extends AuthorizingRealm {
        // 授权
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            System.out.println("执行了=》授权doGetAuthorizationInfo");
            return null;
        }
        //认证
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            System.out.println("认证:=》doGetAuthenticationInfo");
            return null;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    编写配置ShiroConfig,注意启动失败,添加name = "shiroFilterFactoryBean"

    • 创建realm对象,需要自定义类
    • DefaultWebSecurityManager
    • ShiroFilterFactoryBean
    import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class ShiroConfig {
    
        //ShrioFilterFactoryBean
        @Bean(name = "shiroFilterFactoryBean")
        public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
            ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
            // 设置安全管理器
            bean.setSecurityManager(defaultWebSecurityManager);
            return bean;
        }
        //DefaultWebSecurityManager:2
        @Bean
        public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            // 关联UserRealm
            securityManager.setRealm(userRealm);
            return securityManager;
        }
        //创建realm 对象,需要自定义类:1
    //    @Bean(name = "userRealm")
        @Bean
        public UserRealm userRealm(){
            return new UserRealm();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    shiro实现登录拦截

    ShiroConfig中的getShiroFilterFactoryBean方法中添加如下配置

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

    添加拦截成功页面

    • 登录页面login.html

      • DOCTYPE html>
        <html lang="en" xmlns:th="http://www.thymeleaf.org">
        <head>
            <meta charset="UTF-8">
            <title>Titletitle>
        head>
        <body>
        <h1>登录h1>
        <hr>
        <p th:text="${msg}" style="color: red">p>
        <form th:action="@{/login}">
            <p>用户名:<input type="text" name="username">p>
            <p>密码:<input type="text" name="password">p>
            <p><input type="submit">p>
        form>
        body>
        html>
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
    • 在MyController中添加

      • @RequestMapping("/toLogin")
        public String toLogin(){
            return "login";
        }
        
        • 1
        • 2
        • 3
        • 4
    • ShiroConfig中的getShiroFilterFactoryBean方法中添加如下配置

      • //设置登录的请求
        bean.setLoginUrl("/toLogin");
        
        • 1
        • 2

    shiro实现用户验证

    1. MyController中编写用户提交表单之后处理

      @RequestMapping("/login")
      public String login(String username,String password,Model model){
          // 获取当前用户
          Subject subject = SecurityUtils.getSubject();
          // 封装用户的登录数据
          UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
          try {
              subject.login(usernamePasswordToken); // 执行登录的方法,如果没用一场就说明ok了
              return "index";
          } catch (UnknownAccountException e) { //用户名不存在
              model.addAttribute("msg","用户名错误!");
              return "login";
          } catch (IncorrectCredentialsException e){ //密码不存在
              model.addAttribute("msg","密码错误!");
              return "login";
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
    2. login.html的修改

      <!DOCTYPE html>
      <html lang="en" xmlns:th="http://www.thymeleaf.org">
      <head>
          <meta charset="UTF-8">
          <title>Title</title>
      </head>
      <body>
      <h1>登录</h1>
      <hr>
      <p th:text="${msg}" style="color: red"></p>
      <form th:action="@{/login}">
          <p>用户名:<input type="text" name="username"></p>
          <p>密码:<input type="text" name="password"></p>
          <p><input type="submit"></p>
      </form>
      </body>
      </html>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
    3. 用户输入登录信息

    4. 用户认证编写UserRealm中的认证(doGetAuthenticationInfo)

      //认证
      @Override
      protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("认证:=》doGetAuthenticationInfo");
        // 用户名,密码 数据库中取
        String name = "root";
        String password = "123456";
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        if(!token.getUsername().equals(name)){
          return null; // 抛出异常 UnknownAccountException
        }
        // 密码认证,shiro做
        return new SimpleAuthenticationInfo("",password,"");
        //        return null;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15

    shiro整合Mybatis

    1. 导入依赖

      <dependency>
          <groupId>mysqlgroupId>
          <artifactId>mysql-connector-javaartifactId>
          <version>8.0.28version>
      dependency>
      <dependency>
          <groupId>log4jgroupId>
          <artifactId>log4jartifactId>
          <version>1.2.17version>
      dependency>
      <dependency>
          <groupId>com.alibabagroupId>
          <artifactId>druidartifactId>
          <version>1.1.12version>
      dependency>
      
      <dependency>
          <groupId>org.mybatis.spring.bootgroupId>
          <artifactId>mybatis-spring-boot-starterartifactId>
          <version>2.2.2version>
      dependency>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
    2. 配置文件application.yml的编写

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

      package com.stefan.pojo;
      import lombok.AllArgsConstructor;
      import lombok.Data;
      import lombok.NoArgsConstructor;
      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      public class User {
          private int id;
          private String name;
          private String pwd;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
    4. UserMapper.xml映射

      
      DOCTYPE mapper
              PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
              "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
      
      <mapper namespace="com.stefan.mapper.UserMapper">
          <select id="queryUserByName" parameterType="String" resultType="User">
              select * from mybatis.user where name = #{name}
          select>
      mapper>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
    5. UserService接口实现

      package com.stefan.service;
      import com.stefan.pojo.User;
      public interface UserService {
          public User queryUserByName(String name);
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
    6. UserServiceImpl业务逻辑

      package com.stefan.service;
      import com.stefan.mapper.UserMapper;
      import com.stefan.pojo.User;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Service;
      @Service
      public class UserServiceImpl implements UserService {
          @Autowired
          UserMapper userMapper;
          @Override
          public User queryUserByName(String name) {
              return userMapper.queryUserByName(name);
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
    7. 测试环境

      package com.stefan;
      import com.stefan.pojo.User;
      import com.stefan.service.UserServiceImpl;
      import org.junit.jupiter.api.Test;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.boot.test.context.SpringBootTest;
      @SpringBootTest
      class ShiroSpringbootApplicationTests {
          @Autowired
          UserServiceImpl userService;
          @Test
          void contextLoads() {
              User stefan = userService.queryUserByName("stefan");
              System.out.println(stefan);
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
    8. UserRealm连接真实数据库

      //认证
      @Override
      protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        System.out.println("认证:=》doGetAuthenticationInfo");
         UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
       // 连接真实数据库
       User user = userService.queryUserByName(token.getUsername());
       // 密码认证,shiro做
       if(user == null){
         return null;// 抛出异常 UnknownAccountException
       }
       return new SimpleAuthenticationInfo("",user.getPwd(),"");
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
    9. @RequestMapping("/noauth")
      @ResponseBody
      public String unauthorized(){
          return "未经授权无法访问此页面";
      }
      ShiroConfig中的getShiroFilterFactoryBean方法中添加
      
      //设置未授权页面
      bean.setUnauthorizedUrl("/noauth");
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    10. //自定义的UserRealm extends AuthorizingRealm
      public class UserRealm extends AuthorizingRealm {
          @Autowired
          UserService userService;
          // 授权
          @Override
          protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
              System.out.println("执行了=》授权doGetAuthorizationInfo");
              SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
              // 拿到当前登录的这个对象
              Subject subject = SecurityUtils.getSubject();
              User currentUser = (User) subject.getPrincipal(); // 拿到user对象
              //设置当前对象的权限
              info.addStringPermission(currentUser.getPerms());
              return info;
          }
          //认证
          @Override
          protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
              System.out.println("认证:=》doGetAuthenticationInfo");
              UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
              // 连接真实数据库
              User user = userService.queryUserByName(token.getUsername());
              // 密码认证,shiro做
              if(user == null){
                  return null;// 抛出异常 UnknownAccountException
              }
              return new SimpleAuthenticationInfo(user,user.getPwd(),"");
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30

    shiro整合Thymeleaf

    1. shiro-thymeleaf整合包导入——官网

      
      <dependency>
          <groupId>com.github.theborakompanionigroupId>
          <artifactId>thymeleaf-extras-shiroartifactId>
          <version>2.0.0version>
      dependency>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    2. 在ShiroConfig中整合ShiroDialect

      // 整合ShiroDialect:用来整合shiro thymeleaf
      @Bean
      public ShiroDialect getShiroDialect(){
          return new ShiroDialect();
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
    3. index.html页面

      DOCTYPE html>
      <html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
      <head>
          <meta charset="UTF-8">
          <title>Titletitle>
      head>
      <body>
      <h1>首页h1>
      <div th:if="${session.loginUser==null}">
          <a th:href="@{/toLogin}">登录a>
      div>
      <p th:text="${msg}">p>
      <hr>
      <div shiro:hasPermission="user:add">
          <a th:href="@{/user/add}">adda>
      div>
      <div shiro:hasPermission="user:update">
          <a th:href="@{/user/update}">updatea>
      div>
      body>
      html>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21

    ShiroConfig

    package com.stefan.config;
    import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
    import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import java.util.HashMap;
    import java.util.LinkedHashMap;
    import java.util.Map;
    @Configuration
    public class ShiroConfig {
        //ShrioFilterFactoryBean
        @Bean(name = "shiroFilterFactoryBean")
        public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
            ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
            // 设置安全管理器
            bean.setSecurityManager(defaultWebSecurityManager);
            // 添加shiro的内置过滤器
            /*
            * anon:无需认证就可以访问
            * authc:必须认证了才能访问
            * user:必须拥有 记住我 功能才能用
            * perms:拥有对某个资源的权限才能访问
            * role:拥有某个角色权限才能访问
            *
            * */
            Map<String,String> filterChainDefinitionMap = new LinkedHashMap<>();
            //授权,正常情况下,没有授权会跳转到未授权页面
            filterChainDefinitionMap.put("/user/add","perms[user:add]");
            filterChainDefinitionMap.put("/user/update","perms[user:update]");
            // 登录拦截
    //        filterChainDefinitionMap.put("/user/add","authc");
    //        filterChainDefinitionMap.put("/user/update","authc");
            filterChainDefinitionMap.put("/user/*","authc");
            bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
            //设置登录的请求
            bean.setLoginUrl("/toLogin");
            //设置未授权页面
            bean.setUnauthorizedUrl("/noauth");
            return bean;
        }
        //DefaultWebSecurityManager:2
        @Bean
        public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            // 关联UserRealm
            securityManager.setRealm(userRealm);
            return securityManager;
        }
        //创建realm 对象,需要自定义类:1
    //    @Bean(name = "userRealm")
        @Bean
        public UserRealm userRealm(){
            return new UserRealm();
        }
        // 整合ShiroDialect:用来整合shiro thymeleaf
        @Bean
        public ShiroDialect getShiroDialect(){
            return new ShiroDialect();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • UserRealm

      package com.stefan.config;
      import com.stefan.pojo.User;
      import com.stefan.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;
      //自定义的UserRealm extends AuthorizingRealm
      public class UserRealm extends AuthorizingRealm {
          @Autowired
          UserService userService;
          // 授权
          @Override
          protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
              System.out.println("执行了=》授权doGetAuthorizationInfo");
              SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
      //        info.addStringPermission("user:add");
      //        info.addStringPermission("user:update");
              // 拿到当前登录的这个对象
              Subject subject = SecurityUtils.getSubject();
              User currentUser = (User) subject.getPrincipal(); // 拿到user对象
              //设置当前对象的权限
              info.addStringPermission(currentUser.getPerms());
      //        return null;
              return info;
          }
          //认证
          @Override
          protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
              System.out.println("认证:=》doGetAuthenticationInfo");
              // 用户名,密码 数据库中取
      //        String name = "root";
      //        String password = "123456";
      //
      //
      //        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
    UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
    // 连接真实数据库
    User user = userService.queryUserByName(token.getUsername());
    // 密码认证,shiro做
    if(user == null){
       return null;// 抛出异常 UnknownAccountException
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
      package com.stefan.controller;
      import org.apache.shiro.SecurityUtils;
      import org.apache.shiro.authc.IncorrectCredentialsException;
      import org.apache.shiro.authc.UnknownAccountException;
      import org.apache.shiro.authc.UsernamePasswordToken;
      import org.apache.shiro.session.Session;
      import org.apache.shiro.subject.Subject;
      import org.springframework.stereotype.Controller;
      import org.springframework.ui.Model;
      import org.springframework.web.bind.annotation.RequestMapping;
      import org.springframework.web.bind.annotation.ResponseBody;
      @Controller
      public class MyController {
          @RequestMapping({"/","index"})
          public String toIndex(Model model){
              model.addAttribute("msg","hello,Shiro");
              return "index";
          }
          @RequestMapping("/user/add")
          public String add(){
              return "user/add";
          }
          @RequestMapping("/user/update")
          public String update(){
              return "user/update";
          }
          @RequestMapping("/toLogin")
          public String toLogin(){
              return "login";
          }
          @RequestMapping("/login")
          public String login(String username,String password,Model model){
              // 获取当前用户
              Subject subject = SecurityUtils.getSubject();
              // 封装用户的登录数据
              UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);
              try {
                  subject.login(usernamePasswordToken); // 执行登录的方法,如果没用一场就说明ok了
                  return "index";
              } catch (UnknownAccountException e) { //用户名不存在
                  model.addAttribute("msg","用户名错误!");
                  return "login";
              } catch (IncorrectCredentialsException e){ //密码不存在
                  model.addAttribute("msg","密码错误!");
                  return "login";
              }
          }
          @RequestMapping("/noauth")
          @ResponseBody
          public String unauthorized(){
              return "未经授权无法访问此页面";
          }
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • pom依赖

      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
               xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
          <modelVersion>4.0.0</modelVersion>
          <parent>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-parent</artifactId>
              <version>2.6.4</version>
              <relativePath/> <!-- lookup parent from repository -->
          </parent>
          <groupId>com.stefan</groupId>
          <artifactId>shiro-springboot</artifactId>
          <version>0.0.1-SNAPSHOT</version>
          <name>shiro-springboot</name>
          <description>Demo project for Spring Boot</description>
          <properties>
              <java.version>1.8</java.version>
          </properties>
          <dependencies>
              <!--shiro-thymeleaf整合-->
              <dependency>
                  <groupId>com.github.theborakompanioni</groupId>
                  <artifactId>thymeleaf-extras-shiro</artifactId>
                  <version>2.0.0</version>
              </dependency>
              <dependency>
                  <groupId>mysql</groupId>
                  <artifactId>mysql-connector-java</artifactId>
                  <version>8.0.28</version>
              </dependency>
              <dependency>
                  <groupId>log4j</groupId>
                  <artifactId>log4j</artifactId>
                  <version>1.2.17</version>
              </dependency>
              <dependency>
                  <groupId>com.alibaba</groupId>
                  <artifactId>druid</artifactId>
                  <version>1.1.12</version>
              </dependency>
              <dependency>
                  <groupId>org.mybatis.spring.boot</groupId>
                  <artifactId>mybatis-spring-boot-starter</artifactId>
                  <version>2.2.2</version>
              </dependency>
              <!--
                      Subject 用户
                      SecurityManager 管理所有用户
                      Realm 连接数据
              -->
              <!--shiro 整合spring boot-->
              <dependency>
                  <groupId>org.apache.shiro</groupId>
                  <artifactId>shiro-spring-boot-web-starter</artifactId>
                  <version>1.7.1</version>
              </dependency>
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-thymeleaf</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-web</artifactId>
              </dependency>
              <dependency>
                  <groupId>org.projectlombok</groupId>
                  <artifactId>lombok</artifactId>
                  <optional>true</optional>
              </dependency>
              <dependency>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-starter-test</artifactId>
                  <scope>test</scope>
              </dependency>
          </dependencies>
          <build>
              <plugins>
                  <plugin>
                      <groupId>org.springframework.boot</groupId>
                      <artifactId>spring-boot-maven-plugin</artifactId>
                      <configuration>
                          <excludes>
                              <exclude>
                                  <groupId>org.projectlombok</groupId>
                                  <artifactId>lombok</artifactId>
                              </exclude>
                          </excludes>
                      </configuration>
                  </plugin>
              </plugins>
          </build>
      </project>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
      • 22
      • 23
      • 24
      • 25
      • 26
      • 27
      • 28
      • 29
      • 30
      • 31
      • 32
      • 33
      • 34
      • 35
      • 36
      • 37
      • 38
      • 39
      • 40
      • 41
      • 42
      • 43
      • 44
      • 45
      • 46
      • 47
      • 48
      • 49
      • 50
      • 51
      • 52
      • 53
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
      • 69
      • 70
      • 71
      • 72
      • 73
      • 74
      • 75
      • 76
      • 77
      • 78
      • 79
      • 80
      • 81
      • 82
      • 83
      • 84
      • 85
      • 86
      • 87
      • 88
      • 89
      • 90
      • 91
      • 92
    • application配置

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

      DOCTYPE html>
      <html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
      <head>
          <meta charset="UTF-8">
          <title>Titletitle>
      head>
      <body>
      <h1>首页h1>
      <div th:if="${session.loginUser==null}">
          <a th:href="@{/toLogin}">登录a>
      div>
      <p th:text="${msg}">p>
      <hr>
      <div shiro:hasPermission="user:add">
          <a th:href="@{/user/add}">adda>
      div>
      <div shiro:hasPermission="user:update">
          <a th:href="@{/user/update}">updatea>
      div>
      body>
      html>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
      • 21
    • add.html

      DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>Titletitle>
      head>
      <body>
      <h1>addh1>
      body>
      html>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
    • login.html

      DOCTYPE html>
      <html lang="en" xmlns:th="http://www.thymeleaf.org">
      <head>
          <meta charset="UTF-8">
          <title>Titletitle>
      head>
      <body>
      <h1>登录h1>
      <hr>
      <p th:text="${msg}" style="color: red">p>
      <form th:action="@{/login}">
          <p>用户名:<input type="text" name="username">p>
          <p>密码:<input type="text" name="password">p>
          <p><input type="submit">p>
      form>
      body>
      html>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
    • update.html

      DOCTYPE html>
      <html lang="en">
      <head>
          <meta charset="UTF-8">
          <title>Titletitle>
      head>
      <body>
      <h1>updateh1>
      body>
      html>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
    • User实体类

      package com.stefan.pojo;
      import lombok.AllArgsConstructor;
      import lombok.Data;
      import lombok.NoArgsConstructor;
      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      public class User {
          private int id;
          private String name;
          private String pwd;
          private String perms;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
    • UserService

      package com.stefan.service;
      import com.stefan.pojo.User;
      public interface UserService {
          public User queryUserByName(String name);
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
    • UserServiceImpl

      package com.stefan.service;
      import com.stefan.mapper.UserMapper;
      import com.stefan.pojo.User;
      import org.springframework.beans.factory.annotation.Autowired;
      import org.springframework.stereotype.Service;
      @Service
      public class UserServiceImpl implements UserService {
          @Autowired
          UserMapper userMapper;
          @Override
          public User queryUserByName(String name) {
              return userMapper.queryUserByName(name);
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
    • UserMapper

      package com.stefan.mapper;
      import com.stefan.pojo.User;
      import org.apache.ibatis.annotations.Mapper;
      import org.springframework.stereotype.Repository;
      @Repository
      @Mapper
      public interface UserMapper {
          public User queryUserByName(String name);
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    • UserMapper.xml

      
      DOCTYPE mapper
              PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
              "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
      
      <mapper namespace="com.stefan.mapper.UserMapper">
          <select id="queryUserByName" parameterType="String" resultType="User">
              select * from mybatis.user where name = #{name}
          select>
      mapper>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10

    tory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    @Service
    public class UserServiceImpl implements UserService {
    @Autowired
    UserMapper userMapper;
    @Override
    public User queryUserByName(String name) {
    return userMapper.queryUserByName(name);
    }
    }

    
    - UserMapper
    
    ```java
    package com.stefan.mapper;
    import com.stefan.pojo.User;
    import org.apache.ibatis.annotations.Mapper;
    import org.springframework.stereotype.Repository;
    @Repository
    @Mapper
    public interface UserMapper {
        public User queryUserByName(String name);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • UserMapper.xml

      
      DOCTYPE mapper
              PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
              "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
      
      <mapper namespace="com.stefan.mapper.UserMapper">
          <select id="queryUserByName" parameterType="String" resultType="User">
              select * from mybatis.user where name = #{name}
          select>
      mapper>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
  • 相关阅读:
    【笔记:傅里叶分析】数学基础——傅里叶级数与傅里叶变换
    ES6模块化练习import,export
    建筑设计中,如何快速获得场地的等高线图?
    Text embedding 模型总结
    【详细图文】Windows下安装RustRover和配置Rust环境
    【力扣】两数之和 II - 输入有序数组
    Dockerfile文件解释
    【Java 进阶篇】Java Servlet URL Patterns 详解
    2022-06-26 笔记本新机重装系统
    Unity 之 定时调用函数的方法
  • 原文地址:https://blog.csdn.net/liustreh/article/details/126275158