• SpringBoot_15_shiro


    1、Shiro简介

    1.1、Shiro 是什么?

    • Apache Shiro 是 Java 的一个安全(权限)框架。

    • Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在 JavaSE 环境,也可以用在 JavaEE 环境。

    • Shiro 可以完成:认证、授权、加密、会话管理、与Web 集成、缓存等。

    • 下载地址

    1.2、有哪些功能?

    在这里插入图片描述

    • Authentication:身份认证/登录,验证用户是不是拥有相应的身份

    • Authorization:授权,即权限验证,验证某个已认证的用户是否拥有某个权限;即判断用户是否能进行什么操作,如:验证某个用户是否拥有某个角色。或者细粒度的验证某个用户对某个资源是否具有某个权限

    • Session Management:会话管理,即用户登录后就是一次会话,在没有退出之前,它的所有信息都在会话中;会话可以是普通JavaSE环境,也可以是Web 环境的

    • Cryptography:加密,保护数据的安全性,如密码加密存储到数据库,而不是明文存储

    • Web Support:Web 支持,可以非常容易的集成到Web 环境

    • Caching:缓存,比如用户登录后,其用户信息、拥有的角色/权限不必每次去查,这样可以提高效率

    • Concurrency:Shiro支持多线程应用的并发验证,即如在一个线程中开启另一个线程,能把权限自动传播过去

    • Testing:提供测试支持

    • “Run As”:允许一个用户假装为另一个用户(如果他们允许)的身份进行访问

    • Remember Me:记住我,这个是非常常见的功能,即一次登录后,下次再来的话不用登录了

    1.3、Shiro架构(外部)

    从外部来看Shiro,即从应用程序角度的来观察如何使用Shiro完成工作

    在这里插入图片描述

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

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

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

    1.4、Shiro架构(内部)

    在这里插入图片描述

    • 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提高了一些常见的加密组件用于如密码加密/解密。

    2、Hello World

    2.1、快速实践

    • 查看官方文档:http://shiro.apache.org/tutorial.html

    • 官方的quickstart : https://github.com/apache/shiro/tree/master/samples/quickstart/

      在这里插入图片描述

    1. 创建一个maven父工程,用来学习Shiro,删掉不必要的部分

    2. 创建一个普通的Maven子工程:hell-shiro

      在这里插入图片描述

    3. 根据官方文档,我们导入Shiro的依赖

      在这里插入图片描述

      版本号点击这里

      <dependencies>
          <dependency>
              <groupId>org.apache.shirogroupId>
              <artifactId>shiro-coreartifactId>
              <version>1.5.3version>
          dependency>
      
          
          <dependency>
              <groupId>org.slf4jgroupId>
              <artifactId>jcl-over-slf4jartifactId>
              <version>1.7.26version>
          dependency>
          <dependency>
              <groupId>org.slf4jgroupId>
              <artifactId>slf4j-log4j12artifactId>
              <version>1.7.26version>
          dependency>
          <dependency>
              <groupId>log4jgroupId>
              <artifactId>log4jartifactId>
              <version>1.2.17version>
          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
    4. 相关配置文件

      • 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
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
        • 17
        • 18
      • 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
        
        • 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
      • 启动类 Quickstart——官网

        /*
         * Licensed to the Apache Software Foundation (ASF) under one
         * or more contributor license agreements.  See the NOTICE file
         * distributed with this work for additional information
         * regarding copyright ownership.  The ASF licenses this file
         * to you under the Apache License, Version 2.0 (the
         * "License"); you may not use this file except in compliance
         * with the License.  You may obtain a copy of the License at
         *
         *     http://www.apache.org/licenses/LICENSE-2.0
         *
         * Unless required by applicable law or agreed to in writing,
         * software distributed under the License is distributed on an
         * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
         * KIND, either express or implied.  See the License for the
         * specific language governing permissions and limitations
         * under the License.
         */
        
        import org.apache.shiro.SecurityUtils;
        import org.apache.shiro.authc.*;
        import org.apache.shiro.config.IniSecurityManagerFactory;
        import org.apache.shiro.mgt.SecurityManager;
        import org.apache.shiro.session.Session;
        import org.apache.shiro.subject.Subject;
        import org.apache.shiro.util.Factory;
        import org.slf4j.Logger;
        import org.slf4j.LoggerFactory;
        
        
        /**
         * Simple Quickstart application showing how to use Shiro's API.
         * 简单入门Shiro使用API
         *
         * @since 0.9 RC2
         */
        public class Quickstart {
        
            private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);
        
        
            public static void main(String[] args) {
        
                // The easiest way to create a Shiro SecurityManager with configured
                // realms, users, roles and permissions is to use the simple INI config.
                // We'll do that by using a factory that can ingest a .ini file and
                // return a SecurityManager instance:
        
                // Use the shiro.ini file at the root of the classpath
                // (file: and url: prefixes load from files and urls respectively):
                // 读取配置文件:
                Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
                SecurityManager securityManager = factory.getInstance();
        
                // for this simple example quickstart, make the SecurityManager
                // accessible as a JVM singleton.  Most applications wouldn't do this
                // and instead rely on their container configuration or web.xml for
                // webapps.  That is outside the scope of this simple quickstart, so
                // we'll just do the bare minimum so you can continue to get a feel
                // for things.
                SecurityUtils.setSecurityManager(securityManager);
        
                // Now that a simple Shiro environment is set up, let's see what you can do:
        
                // get the currently executing user:
                // 获取当前的用户对象 Subject
                Subject currentUser = SecurityUtils.getSubject();
        
                // Do some stuff with a Session (no need for a web or EJB container!!!)
                //通过当前用户拿到Shiro的Session 可以脱离web存值取值
                Session session = currentUser.getSession();
                session.setAttribute("someKey", "aValue");
                String value = (String) session.getAttribute("someKey");
                if (value.equals("aValue")) {
                    log.info("Retrieved the correct value! [" + value + "]");
                }
        
                // let's login the current user so we can check against roles and permissions:
                //判断当前的用户是否被认证
                if (!currentUser.isAuthenticated()) {
                    //Token 令牌
                    UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
                    //设置记住我
                    token.setRememberMe(true);
                    try {
                        //执行登录操作
                        currentUser.login(token);
                    } catch (UnknownAccountException uae) {
                        log.info("There is no user with username of " + token.getPrincipal());
                    } catch (IncorrectCredentialsException ice) {
                        log.info("Password for account " + token.getPrincipal() + " was incorrect!");
                    } catch (LockedAccountException lae) {
                        log.info("The account for username " + token.getPrincipal() + " is locked.  " +
                                "Please contact your administrator to unlock it.");
                    }
                    // ... catch more exceptions here (maybe custom ones specific to your application?
                    catch (AuthenticationException ae) {
                        //unexpected condition?  error?
                    }
                }
        
                //say who they are:
                //print their identifying principal (in this case, a username):
                log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
        
                //test a role:
                // 检查角色
                if (currentUser.hasRole("schwartz")) {
                    log.info("May the Schwartz be with you!");
                } else {
                    log.info("Hello, mere mortal.");
                }
        
                //test a typed permission (not instance-level)
                //粗粒度
                if (currentUser.isPermitted("lightsaber:wield")) {
                    log.info("You may use a lightsaber ring.  Use it wisely.");
                } else {
                    log.info("Sorry, lightsaber rings are for schwartz masters only.");
                }
        
                //a (very powerful) Instance Level permission:
                //细粒度
                if (currentUser.isPermitted("winnebago:drive:eagle5")) {
                    log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'.  " +
                            "Here are the keys - have fun!");
                } else {
                    log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
                }
        
                //all done - log out!
                //注销
                currentUser.logout();
        
                //结束
                System.exit(0);
            }
        }
        
        • 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
        • 93
        • 94
        • 95
        • 96
        • 97
        • 98
        • 99
        • 100
        • 101
        • 102
        • 103
        • 104
        • 105
        • 106
        • 107
        • 108
        • 109
        • 110
        • 111
        • 112
        • 113
        • 114
        • 115
        • 116
        • 117
        • 118
        • 119
        • 120
        • 121
        • 122
        • 123
        • 124
        • 125
        • 126
        • 127
        • 128
        • 129
        • 130
        • 131
        • 132
        • 133
        • 134
        • 135
        • 136
        • 137
        • 138

        在这里插入图片描述

      • Spring Secutrry都有~(只是换了个名字)

        // 获取当前的用户对象 Subject
        Subject currentUser = SecurityUtils.getSubject();
        Session session = currentUser.getSession();
        currentUser.isAuthenticated()
            currentUser.getPrincipal()
            currentUser.hasRole("schwartz")
            currentUser.isPermitted("lightsaber:wield")
            currentUser.logout();
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8

    3、SpringBoot集成

    3.1、SpringBoot整合Shiro环境搭建

    1. 新建一个项目或模块,勾选依赖

      在这里插入图片描述

      pom.xml

      <dependencies>
          
          <dependency>
              <groupId>org.springframework.bootgroupId>
              <artifactId>spring-boot-starter-thymeleafartifactId>
          dependency>
      
          <dependency>
              <groupId>org.springframework.bootgroupId>
              <artifactId>spring-boot-starter-webartifactId>
          dependency>
      
          <dependency>
              <groupId>org.springframework.bootgroupId>
              <artifactId>spring-boot-starter-testartifactId>
              <scope>testscope>
              <exclusions>
                  <exclusion>
                      <groupId>org.junit.vintagegroupId>
                      <artifactId>junit-vintage-engineartifactId>
                  exclusion>
              exclusions>
          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
    2. 测试环境是否正常

      • 新建一个controller页面

        @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
      • 新建一个index.html页面

        DOCTYPE html>
        <html lang="en" xmlns:th="http://www.thymeleaf.org">
        <head>
            <meta charset="UTF-8">
            <title>首页title>
        head>
        <body>
        <div>
            <h1>首页h1>
            <p th:text="${msg}">p>
        
            <hr>
            <a th:href="@{/user/add}">adda>   | <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
      • 新建一个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
      • 项目结构

    在这里插入图片描述

    • 运行截图

      在这里插入图片描述

    1. 导入shiro整合spring的包——官网,查看最新版本

      
      
      
      <dependency>
          <groupId>org.apache.shirogroupId>
          <artifactId>shiro-springartifactId>
          <version>1.9.1version>
      dependency>
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
    2. 编写导入配置类

      • 编写一个自定义类UserRealm

        //自定义的UserRealm
        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("执行了=>认证doGetAuthorizationInfo");
                return null;
            }
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
        • 6
        • 7
        • 8
        • 9
        • 10
        • 11
        • 12
        • 13
        • 14
        • 15
        • 16
      • 编写配置ShiroConfig

        • 创建realm对象,需要自定义类
        • DefaultWebSecurityManager
        • ShiroFilterFactoryBean
        @Configuration
        public class ShiroConfig {
        
            //3. shiroFilterFactoryBean
        
            @Bean
            public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
                ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
                // 设置安全管理器
                bean.setSecurityManager(defaultWebSecurityManager);
        
                return bean;
            }
        
            //2. DefaultWebSecurityManager
        
            @Bean
            public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
                DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        
                // 关联userRealm
                securityManager.setRealm(userRealm);
                return securityManager;
            }
            //1. 创建realm对象,需要自定义类
        
            @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

    3.2、Shiro实现登录拦截

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

      • anon: 无需认证就可以访问
      • authc: 必须认证了才能访问
      • user: 必须拥有记住我功能才能用
      • perms: 拥有对某个资源的权限才能访问
      • role: 拥有某个角色权限
      Map<String, String> filterMap = new LinkedHashMap<>();
      filterMap.put("/user/add","authc");
      filterMap.put("/user/update","authc");
      bean.setFilterChainDefinitionMap(filterMap);
      
      • 1
      • 2
      • 3
      • 4
    • 点击首页的add或者update之后

      在这里插入图片描述

    • 添加拦截成功页面

      • 登录页面login.html

        DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <title>登录页面title>
        head>
        <body>
        <h1>登录h1>
        <hr>
        
        <form action="">
            <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
      • 在MyConfig中添加

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

        //设置登录的请求
        bean.setLoginUrl("/toLogin");
        
        • 1
        • 2
    • 拦截成功页面

      在这里插入图片描述

    3.3、Shiro实现用户认证

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

      @RequestMapping("/login")
      public String login(String username, String password, Model model) {
          //获取一个用户
          Subject subject = SecurityUtils.getSubject();
          // 封装用户的登录数据
          UsernamePasswordToken token = new UsernamePasswordToken(username, password);
      
          try {
              subject.login(token);//执行登录的方法,如果没有异常就说明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
      • 18
      • 19
    2. login.html的修改

      DOCTYPE html>
      <html lang="en" xmlns:th="http://www.thymeleaf.org">
      <head>
          <meta charset="UTF-8">
          <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
      • 18
    3. 用户输入登录信息

      • 页面

        在这里插入图片描述

      • 控制台

        在这里插入图片描述

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

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

    3.4、Shiro整合Mybatis

    1. 导入依赖

      <dependency>
          <groupId>org.projectlombokgroupId>
          <artifactId>lombokartifactId>
      dependency>
      <dependency>
          <groupId>mysqlgroupId>
          <artifactId>mysql-connector-javaartifactId>
      dependency>
      
      <dependency>
          <groupId>log4jgroupId>
          <artifactId>log4jartifactId>
          <version>1.2.17version>
      dependency>
      
      <dependency>
          <groupId>com.alibabagroupId>
          <artifactId>druid-spring-boot-starterartifactId>
          <version>1.2.11version>
      dependency>
      
      
      <dependency>
          <groupId>org.mybatis.spring.bootgroupId>
       <artifactId>mybatis-spring-boot-starterartifactId>
          <version>2.1.3version>
      dependency>
      
      • 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
    2. 配置文件application.yml的编写

      spring:
        datasource:
          username: root
          password: '0000'
          #?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
          druid:
            # 数据库连接池最大值
            max-active: 20
            # 数据库连接池初始值
            initial-size: 5
            # 数据库连接池最小空闲值
            min-idle: 5
            # 池中空闲连接大于minIdle且连接空闲时间大于该值,则关闭该连接,单位毫秒(5分钟,默认30分钟)
            min-evictable-idle-time-millis: 300000
            # 获取连接时最大等待时间,单位毫秒(1分钟)
            max-wait: 60000
            # 检测连接是否有效时执行的sql命令
            validation-query: select 1
            # 借用连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
            test-on-borrow: false
            # 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
            test-on-return: false
            # 连接空闲时检测,如果连接空闲时间大于timeBetweenEvictionRunsMillis指定的毫秒,执行validationQuery指定的SQL来检测连接是否有效
            test-while-idle: true
            # 空闲连接检查、废弃连接清理、空闲连接池大小调整的操作时间间隔,单位是毫秒(1分钟)
            time-between-eviction-runs-millis: 60000
            # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
            filters: stat,wall
            # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
            connection-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
            #是否启用StatFilter默认值false,用于采集 web-jdbc 关联监控的数据。
            web-stat-filter:
              enabled: true
              #需要监控的 url
              url-pattern: /*
              #排除一些静态资源,以提高效率
              exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'
            #是否启用StatViewServlet(监控页面)默认值为false(考虑到安全问题默认并未启动,如需启用建议设置密码或白名单以保障安全)
            stat-view-servlet:
              enabled: true
              #内置的监控页面地址,例如 /druid/*,则内置监控页面的首页是 /druid/index.html
              url-pattern: /druid/*
              #是否允许清空统计数据
              reset-enable: false
              login-username: admin
              login-password: admin
      
      • 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
    3. User类的编写

      @Data
      @AllArgsConstructor
      @NoArgsConstructor
      public class User {
          private int id;
          private String name;
          private String pwd;
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    4. UserMapper.xml映射

      
      DOCTYPE mapper
           PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
              "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
      
      <mapper namespace="com.lee.mapper.UserMapper">
          
          <select id="queryUserList" resultType="User">
              select * from mybatis.user;
          select>
      
          <select id="queryUserById" resultType="User">
              select * from mybatis.user where id = #{id};
          select>
      
          <insert id="addUser" parameterType="User">
              insert into mybatis.user (id, name, pwd) values (#{id},#{name},#{pwd});
          insert>
      
          <update id="updateUser" parameterType="User">
              update mybatis.user set name=#{name},pwd = #{pwd} where id = #{id};
          update>
      
          <delete id="deleteUser" parameterType="int">
              delete from mybatis.user where id = #{id}
          delete>
      mapper>
      
      • 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
    5. UserMapper

      @Repository
      @Mapper
      public interface UserMapper {
       	public User queryUserByName(String name);
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
    6. UserService接口实现

      public interface UserService {
      
          public User queryUserByName(String name);
      }
      
      • 1
      • 2
      • 3
      • 4
    7. UserServiceImpl业务逻辑

      @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
    8. 测试环境

      @SpringBootTest
      class ShiroSpringbootApplicationTests {
      
          @Autowired
          UserService userService;
          @Test
          void contextLoads() {
              System.out.println(userService.queryUserByName("小明"));
          }
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10

    在这里插入图片描述

    1. UserRealm连接真实数据库

      //认证
      @Override
      protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
          System.out.println("执行了=>认证doGetAuthorizationInfo");
      
          UsernamePasswordToken userToken = (UsernamePasswordToken) token;
          
          // 真实数据库 用户名、密码, 数据中取
          User user = userService.queryUserByName(userToken.getUsername());
      
          if (user == null) {//没有这个人
              return null;
          }
      
          // 密码认证,shiro做
          return new SimpleAuthenticationInfo("",user.getPwd(),"");
      }
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17

      在这里插入图片描述

    2. 断点测试密码加密类型

    • 打断点Debug

      在这里插入图片描述

    • 默认是SimpleCredentialsMatcher加密

      在这里插入图片描述

    • MD5加密——测试

      123456——E10ADC3949BA59ABBE56E057F20F883E

    • MD5盐值加密

    • 所有加密

      在这里插入图片描述

    3.5、Shiro实现用户授权

    1. ShiroConfig中的getShiroFilterFactoryBean方法添加认证代码

      //授权,正常情况下,没有授权会跳转到为授权页面
      filterMap.put("/user/add","perms[user:add]");
      filterMap.put("/user/update","perms[user:update]");
      
      • 1
      • 2
      • 3
    2. 登录之后点击add按钮会弹出如下页面

      在这里插入图片描述

    3. 添加为授权页面

      • MyController

        @RequestMapping("/noauto")
        @ResponseBody
        public String unauthorized() {
            return "未经授权,无法访问此页面";
        }
        
        • 1
        • 2
        • 3
        • 4
        • 5
      • ShiroConfig中的getShiroFilterFactoryBean方法中添加

        //为授权页面
        bean.setUnauthorizedUrl("/noauto");
        
        • 1
        • 2
    4. 再次测试

      在这里插入图片描述

      在这里插入图片描述

      所以需要在UserRealm中为用户进行真正授权

    5. UserRealm类的修改

      //自定义的UserRealm
      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 token) throws AuthenticationException {
              ......
              // 密码认证,shiro做
              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
      • 31
    6. 再次测试

      在这里插入图片描述

    3.6、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>首页title>
      head>
      <body>
      
      <div>
          <h1>首页h1>
          <p th:text="${msg}">p>
      
          
          
      
          <div shiro:notAuthenticated>
              <a th:href="@{/toLogin}">登录a>
          div>
      
          <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>
      
      div>
      body>
      html>
      
      • 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
    4. 页面显示

      在这里插入图片描述

    3.7、所有代码

    • ShiroConfig

      package com.lee.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.LinkedHashMap;
      import java.util.Map;
      
      @Configuration
      public class ShiroConfig {
      
          //shiroFilterFactoryBean
      
          @Bean
          public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
              ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
              // 设置安全管理器
              bean.setSecurityManager(defaultWebSecurityManager);
      
              // 添加shiro的内置过滤器
              /*
                  anon: 无需认证就可以访问
                  authc: 必须认证了才能访问
                  user: 必须拥有记住我功能才能用
                  perms: 拥有对某个资源的权限才能访问
                  role: 拥有某个角色权限
               */
      
              //拦截
              Map<String, String> filterMap = new LinkedHashMap<>();
              //filterMap.put("/user/add","authc");
              //filterMap.put("/user/update","authc");
      
      
              //授权,正常情况下,没有授权会跳转到为授权页面
              filterMap.put("/user/add","perms[user:add]");
              filterMap.put("/user/update","perms[user:update]");
      
              filterMap.put("/user/*","authc");
      
              bean.setFilterChainDefinitionMap(filterMap);
      
              //设置登录的请求
              bean.setLoginUrl("/toLogin");
      
              //为授权页面
              bean.setUnauthorizedUrl("/noauto");
      
            return bean;
          }
      
          //DefaultWebSecurityManager
      
          @Bean
          public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
              DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
      
              // 关联userRealm
              securityManager.setRealm(userRealm);
              return securityManager;
          }
          //创建realm对象,需要自定义类
      
          @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
      • 63
      • 64
      • 65
      • 66
      • 67
      • 68
      • 69
      • 70
      • 71
      • 72
      • 73
      • 74
      • 75
      • 76
      • 77
      • 78
    • UserRealm

      package com.lee.config;
      
      import com.lee.pojo.User;
      import com.lee.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
      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");
      
              //拿到当前登录的这个对象
              Subject subject = SecurityUtils.getSubject();
              User currentUser = (User)subject.getPrincipal();//拿到user对象
      
              //设置当前用户的权限
              info.addStringPermission(currentUser.getPerms());
      
              return info;
          }
      
          //认证
          @Override
          protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
              System.out.println("执行了=>认证doGetAuthorizationInfo");
      
              UsernamePasswordToken userToken = (UsernamePasswordToken) token;
      
              // 虚拟用户
              //String name = "root";
              //String password = "123456";
              //if (!userToken.getUsername().equals(name)) {
              //    return null;//抛出异常 UnknownAccountException
              //}
      
              // 真实数据库 用户名、密码, 数据中取
              User user = userService.queryUserByName(userToken.getUsername());
      
              if (user == null) {//没有这个人
                  return null;
              }
      
              //首页
              //Subject currentSubject = SecurityUtils.getSubject();
              //Session session = currentSubject.getSession();
              //session.setAttribute("loginUser",user);
      
      
              // 密码认证,shiro做
              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
      • 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
    • MyController

      package com.lee.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.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 token = new UsernamePasswordToken(username, password);
      
              try {
                  subject.login(token);//执行登录的方法,如果没有异常就说明ok了
                  return "index";
              } catch (UnknownAccountException e) {//用户名不存在
                  model.addAttribute("msg","用户名错误");
                  return "login";
              } catch (IncorrectCredentialsException e) {//密码不存在
                  model.addAttribute("msg","密码错误");
                  return "login";
              }
          }
      
          @RequestMapping("/noauto")
          @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
      • 54
      • 55
      • 56
      • 57
      • 58
      • 59
      • 60
      • 61
      • 62
    • pom依赖

      
      <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.0modelVersion>
          <groupId>nuc.ssgroupId>
          <artifactId>shiro-springbootartifactId>
          <version>0.0.1-SNAPSHOTversion>
          <name>shiro-springbootname>
          <description>Demo project for Spring Bootdescription>
      
          <properties>
              <java.version>1.8java.version>
              <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
              <project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
              <spring-boot.version>2.3.0.RELEASEspring-boot.version>
          properties>
      
          <dependencies>
      
              
      
              
              
              <dependency>
                  <groupId>com.github.theborakompanionigroupId>
                  <artifactId>thymeleaf-extras-shiroartifactId>
                  <version>2.0.0version>
              dependency>
      
              <dependency>
                  <groupId>org.projectlombokgroupId>
                  <artifactId>lombokartifactId>
              dependency>
              <dependency>
                  <groupId>mysqlgroupId>
                  <artifactId>mysql-connector-javaartifactId>
              dependency>
      
              <dependency>
                  <groupId>log4jgroupId>
                  <artifactId>log4jartifactId>
                  <version>1.2.17version>
              dependency>
      
              <dependency>
                  <groupId>com.alibabagroupId>
                  <artifactId>druidartifactId>
                  <version>1.1.23version>
              dependency>
      
              
              <dependency>
                  <groupId>org.mybatis.spring.bootgroupId>
                  <artifactId>mybatis-spring-boot-starterartifactId>
                  <version>2.1.3version>
              dependency>
      
              
              <dependency>
                  <groupId>org.apache.shirogroupId>
                  <artifactId>shiro-springartifactId>
                  <version>1.5.3version>
              dependency>
              
              <dependency>
                  <groupId>org.springframework.bootgroupId>
                  <artifactId>spring-boot-starter-thymeleafartifactId>
              dependency>
      
              <dependency>
                  <groupId>org.springframework.bootgroupId>
                  <artifactId>spring-boot-starter-webartifactId>
              dependency>
      
              <dependency>
                  <groupId>org.springframework.bootgroupId>
                  <artifactId>spring-boot-starter-testartifactId>
                  <scope>testscope>
                  <exclusions>
                      <exclusion>
                          <groupId>org.junit.vintagegroupId>
                          <artifactId>junit-vintage-engineartifactId>
                      exclusion>
                  exclusions>
              dependency>
          dependencies>
      
          <dependencyManagement>
              <dependencies>
                  <dependency>
                      <groupId>org.springframework.bootgroupId>
                      <artifactId>spring-boot-dependenciesartifactId>
                      <version>${spring-boot.version}version>
                      <type>pomtype>
                      <scope>importscope>
                  dependency>
              dependencies>
          dependencyManagement>
      
          <build>
              <plugins>
                  <plugin>
                      <groupId>org.apache.maven.pluginsgroupId>
                      <artifactId>maven-compiler-pluginartifactId>
                      <configuration>
                          <source>1.8source>
                          <target>1.8target>
                          <encoding>UTF-8encoding>
                      configuration>
                  plugin>
                  <plugin>
                      <groupId>org.springframework.bootgroupId>
                      <artifactId>spring-boot-maven-pluginartifactId>
                  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
      • 93
      • 94
      • 95
      • 96
      • 97
      • 98
      • 99
      • 100
      • 101
      • 102
      • 103
      • 104
      • 105
      • 106
      • 107
      • 108
      • 109
      • 110
      • 111
      • 112
      • 113
      • 114
      • 115
      • 116
      • 117
      • 118
      • 119
      • 120
      • 121
      • 122
      • 123

    4、完美的解释

    让 Apache Shiro 保护你的应用

  • 相关阅读:
    Day40 网络编程(二) InetAddress类, Socket ,TCP编程,UDP编程
    【Vue-Element】Vue下配置好看的滚动条
    Java 面试题 —— 请你介绍一下 Java 的线程池
    短视频解析接口分发系统
    《Effective Java》知识点(9)--异常
    Ajax学习:原生jsonp实践
    Linux性能优化实战CPU篇之总结(四)
    Linux系统切换用户后只显示$问题解决
    记一次MySQL执行修改语句超时问题
    go初识iris框架(六) - session使用和控制
  • 原文地址:https://blog.csdn.net/ayoM_/article/details/126761720