Java安全框架——Shiro的使用详解(附springboot整合Shiro的demo)

作者:つ九键按三下 时间:2022-05-29 09:46:46 

目录
  • Shiro简介

  • Shiro快速入门

  • SpringBoot-Shiro整合(最后会附上完整代码)

    • 附上最后的完整代码

  • Shiro整合mybatis

    • 认证搞完了,我们再来看看授权

      • Shiro-thymeleaf整合

        Shiro简介

        1. Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理

        2. 三个核心组件:Subject, SecurityManager 和 Realms

        3. Subject代表了当前用户的安全操作

        4. SecurityManager管理所有用户的安全操作,是Shiro框架的核心,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。

        5. Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。

        6. Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。

        Shiro快速入门

        导入依赖


               <dependency>
                   <groupId>org.apache.shiro</groupId>
                   <artifactId>shiro-core</artifactId>
                   <version>1.7.1</version>
               </dependency>

        <!-- configure logging -->
               <!-- https://mvnrepository.com/artifact/org.slf4j/jcl-over-slf4j -->
               <dependency>
                   <groupId>org.slf4j</groupId>
                   <artifactId>jcl-over-slf4j</artifactId>
                   <version>2.0.0-alpha1</version>
               </dependency>
               <dependency>
                   <groupId>org.slf4j</groupId>
                   <artifactId>slf4j-log4j12</artifactId>
                   <version>2.0.0-alpha1</version>
               </dependency>
               <dependency>
                   <groupId>log4j</groupId>
                   <artifactId>log4j</artifactId>
                   <version>1.2.17</version>
               </dependency>

        配置log4j.properties


        log4j.rootLogger=INFO, stdout

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

        # General Apache libraries
        log4j.logger.org.apache=WARN

        # Spring
        log4j.logger.org.springframework=WARN

        # Default Shiro logging
        log4j.logger.org.apache.shiro=INFO

        # Disable verbose logging
        log4j.logger.org.apache.shiro.util.ThreadContext=WARN
        log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN

        配置Shiro.ini(在IDEA中需要导入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

        快速入门实现类 quickStart.java


        import org.apache.shiro.SecurityUtils;
        import org.apache.shiro.authc.*;
        import org.apache.shiro.config.IniSecurityManagerFactory;
        import org.apache.shiro.mgt.DefaultSecurityManager;
        import org.apache.shiro.realm.text.IniRealm;
        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;

        public class quickStart {

        private static final transient Logger log = LoggerFactory.getLogger(quickStart.class);

        /*
               Shiro三大对象:
                       Subject: 用户
                       SecurityManager:管理所有用户
                       Realm: 连接数据
            */

        public static void main(String[] args) {

        // 创建带有配置的Shiro SecurityManager的最简单方法
               // realms, users, roles and permissions 是使用简单的INI配置。
               // 我们将使用可以提取.ini文件的工厂来完成此操作,
               // 返回一个SecurityManager实例:

        // 在类路径的根目录下使用shiro.ini文件
               // (file:和url:前缀分别从文件和url加载):
               //Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
               //SecurityManager securityManager = factory.getInstance();
               DefaultSecurityManager securityManager = new DefaultSecurityManager();
               IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
               securityManager.setRealm(iniRealm);

        // 对于这个简单的示例快速入门,请使SecurityManager
               // 可作为JVM单例访问。大多数应用程序都不会这样做
               // 而是依靠其容器配置或web.xml进行
               // webapps。这超出了此简单快速入门的范围,因此
               // 我们只做最低限度的工作,这样您就可以继续感受事物.
               SecurityUtils.setSecurityManager(securityManager);

        // 现在已经建立了一个简单的Shiro环境,让我们看看您可以做什么:

        // 获取当前用户对象 Subject
               Subject currentUser = SecurityUtils.getSubject();

        // 使用Session做一些事情(不需要Web或EJB容器!!!
               Session session = currentUser.getSession();//通过当前用户拿到Session
               session.setAttribute("someKey", "aValue");
               String value = (String) session.getAttribute("someKey");
               if (value.equals("aValue")) {
                   log.info("Retrieved the correct value! [" + value + "]");
               }

        // 判断当前用户是否被认证
               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 (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);//退出
           }
        }

        启动测试

        Java安全框架——Shiro的使用详解(附springboot整合Shiro的demo)

        SpringBoot-Shiro整合(最后会附上完整代码)

        前期工作

        导入shiro-spring整合包依赖


        <!--  shiro-spring整合包 -->
        <dependency>
           <groupId>org.apache.shiro</groupId>
           <artifactId>shiro-spring</artifactId>
           <version>1.7.1</version>
        </dependency>

        跳转的页面
        index.html


        <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
        <head>
           <meta charset="UTF-8">
           <title>首页</title>
        </head>
        <body>

        <h1>首页</h1>

        <p th:text="${msg}"></p>

        <a th:href="@{/user/add}" rel="external nofollow"  rel="external nofollow"  rel="external nofollow" >add</a>| <a th:href="@{/user/update}" rel="external nofollow"  rel="external nofollow"  rel="external nofollow" >update</a>

        </body>
        </html>

        add.html


        <!DOCTYPE html>
        <html lang="en">
        <head>
           <meta charset="UTF-8">
           <title>add</title>
        </head>
        <body>
        <p>add</p>
        </body>
        </html>

        update.html


        <!DOCTYPE html>
        <html lang="en">
        <head>
           <meta charset="UTF-8">
           <title>update</title>
        </head>
        <body>
        <p>update</p>
        </body>
        </html>

        编写shiro的配置类ShiroConfig.java


        package com.example.config;

        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 {
           //3. ShiroFilterFactoryBean
           @Bean
           public ShiroFilterFactoryBean getshiroFilterFactoryBean(@Qualifier("SecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
               ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
               //设置安全管理器
               factoryBean.setSecurityManager(defaultWebSecurityManager);

        return factoryBean;
           }

        //2.创建DefaultWebSecurityManager
           @Bean(name = "SecurityManager")
           public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
               DefaultWebSecurityManager SecurityManager=new DefaultWebSecurityManager();
               //3.关联Realm
               SecurityManager.setRealm(userRealm);
               return SecurityManager;
           }
           //1.创建Realm对象
           @Bean(name = "userRealm")
           public UserRealm userRealm(){
               return new UserRealm();
           }

        }

        编写UserRealm.java


        package com.example.config;

        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("授权");
               return null;
           }

        @Override
           protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
               System.out.println("认证");
               return null;
           }
        }

        编写controller测试环境是否搭建好


        package com.example.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 index(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";
           }
        }

        Java安全框架——Shiro的使用详解(附springboot整合Shiro的demo)

        实现登录拦截

        在ShiroConfig.java文件中添加拦截


        Map<String,String> filterMap = new LinkedHashMap<>();
               //对/user/*下的文件只有拥有authc权限的才能访问
               filterMap.put("/user/*","authc");
               //将Map存放到ShiroFilterFactoryBean中
               factoryBean.setFilterChainDefinitionMap(filterMap);

        这样,代码跑起来,你点击add或者update就会出现404错误,这时候,我们再继续添加,让它跳转到我们自定义的登录页

        添加登录拦截到登录页


               //需进行权限认证时跳转到toLogin
               factoryBean.setLoginUrl("/toLogin");
               //权限认证失败时跳转到unauthorized
               factoryBean.setUnauthorizedUrl("/unauthorized");

        login.html


        <!DOCTYPE html>
        <html lang="en">
        <head>
           <meta charset="UTF-8">
           <title>登录</title>
        </head>
        <body>
        <form action="">
           用户名:<input type="text" name="username"><br>
           密码:<input type="text" name="password"><br>
           <input type="submit">
        </form>

        </body>
        </html>

        视图跳转添加一个login页面跳转


           @RequestMapping("/toLogin")
           public String login(){
               return "login";
           }

        上面,我们已经成功拦截了,现在我们来实现用户认证

        首先,我们需要一个登录页面

        login.html


        <!DOCTYPE html>
        <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
        <head>
           <meta charset="UTF-8">
           <title>登录</title>
        </head>
        <body>
        <p th:text="${msg}" style="color: red"></p>
        <form th:action="@{/login}">
           用户名:<input type="text" name="username"><br>
           密码:<input type="text" name="password"><br>
           <input type="submit">
        </form>

        </body>
        </html>

        其次,去controller编写跳转到登录页面


           @RequestMapping("/login")
           public String login(String username,String password,Model model){
               //获得当前的用户
               Subject subject = SecurityUtils.getSubject();
               //封装用户数据
               UsernamePasswordToken taken = new UsernamePasswordToken(username,password);

        try{//执行登陆操作,没有发生异常就说明登陆成功
                   subject.login(taken);
                   return "index";
               }catch (UnknownAccountException e){
                   model.addAttribute("msg","用户名错误");
                   return "login";
               }catch (IncorrectCredentialsException e){
                   model.addAttribute("msg","密码错误");
                   return "login";
               }

        }

        最后去UserRealm.java配置认证


          //认证
           @Override
           protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
               System.out.println("认证");

        String name = "root";
               String password = "123456";

        UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;

        if (!userToken.getUsername().equals(name)){
                   return null;//抛出异常  用户名错误那个异常
               }

        //密码认证,shiro自己做
               return new SimpleAuthenticationInfo("",password,"");
           }

        运行测试,成功!!!

        附上最后的完整代码

        pom.xml引入的依赖

        pom.xml


        <?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.4.4</version>
               <relativePath/> <!-- lookup parent from repository -->
           </parent>
           <groupId>com.example</groupId>
           <artifactId>springboot-08-shiro</artifactId>
           <version>0.0.1-SNAPSHOT</version>
           <name>springboot-08-shiro</name>
           <description>Demo project for Spring Boot</description>
           <properties>
               <java.version>1.8</java.version>
           </properties>
           <dependencies>

        <!--  shiro-spring整合包 -->
               <dependency>
                   <groupId>org.apache.shiro</groupId>
                   <artifactId>shiro-spring</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.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>
                   </plugin>
               </plugins>
           </build>

        </project>

        静态资源

        index.html


        <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
        <head>
           <meta charset="UTF-8">
           <title>首页</title>
        </head>
        <body>

        <h1>首页</h1>

        <p th:text="${msg}"></p>

        <a th:href="@{/user/add}" rel="external nofollow"  rel="external nofollow"  rel="external nofollow" >add</a>| <a th:href="@{/user/update}" rel="external nofollow"  rel="external nofollow"  rel="external nofollow" >update</a>

        </body>
        </html>

        login.html


        <!DOCTYPE html>
        <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
        <head>
           <meta charset="UTF-8">
           <title>登录</title>
        </head>
        <body>
        <p th:text="${msg}" style="color: red"></p>
        <form th:action="@{/login}">
           用户名:<input type="text" name="username"><br>
           密码:<input type="text" name="password"><br>
           <input type="submit">
        </form>

        </body>
        </html>

        add.html


        <!DOCTYPE html>
        <html lang="en">
        <head>
           <meta charset="UTF-8">
           <title>add</title>
        </head>
        <body>
        <p>add</p>
        </body>
        </html>

        update.html


        <!DOCTYPE html>
        <html lang="en">
        <head>
           <meta charset="UTF-8">
           <title>update</title>
        </head>
        <body>
        <p>update</p>
        </body>
        </html>

        controller层

        MyController.java


        package com.example.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;

        @Controller
        public class MyController {

        @RequestMapping({"/","/index"})
           public String index(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 taken = new UsernamePasswordToken(username,password);

        try{//执行登陆操作,没有发生异常就说明登陆成功
                   subject.login(taken);
                   return "index";
               }catch (UnknownAccountException e){
                   model.addAttribute("msg","用户名错误");
                   return "login";
               }catch (IncorrectCredentialsException e){
                   model.addAttribute("msg","密码错误");
                   return "login";
               }

        }

        }

        config文件

        ShiroConfig.java


        package com.example.config;

        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 {
           //4. ShiroFilterFactoryBean
           @Bean
           public ShiroFilterFactoryBean getshiroFilterFactoryBean(@Qualifier("SecurityManager") DefaultWebSecurityManager defaultWebSecurityManager){
               ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
               //5. 设置安全管理器
               factoryBean.setSecurityManager(defaultWebSecurityManager);

        /*  shiro内置过滤器
                   anon无需授权、登录就可以访问,所有人可访。
                   authc 需要登录授权才能访问。
                   authcBasicBasic HTTP身份验证 *
                   logout退出 * 。退出成功后,会 redirect到设置的/URI
                   noSessionCreation不创建会话连接器
                   perms授权 * ,拥有对某个资源的权限才可访问
                   port端口 *
                   restrest风格 *
                   roles角色 * ,拥有某个角色的权限才可访问
                   sslssl * 。通过https协议才能通过
                   user用户 * ,需要有remember me功能方可使用
                */
               Map<String,String> filterMap = new LinkedHashMap<>();
               //对/user/*下的文件只有拥有authc权限的才能访问
               filterMap.put("/user/*","authc");
               //将Map存放到ShiroFilterFactoryBean中
               factoryBean.setFilterChainDefinitionMap(filterMap);
               //需进行权限认证时跳转到toLogin
               factoryBean.setLoginUrl("/toLogin");
               //权限认证失败时跳转到unauthorized
               factoryBean.setUnauthorizedUrl("/unauthorized");

        return factoryBean;
           }

        //2.创建DefaultWebSecurityManager
           @Bean(name = "SecurityManager")
           public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
               DefaultWebSecurityManager SecurityManager=new DefaultWebSecurityManager();
               //3.关联Realm
               SecurityManager.setRealm(userRealm);
               return SecurityManager;
           }
           //1.创建Realm对象
           @Bean(name = "userRealm")
           public UserRealm userRealm(){
               return new UserRealm();
           }

        }

        UserRealm.java


        package com.example.config;

        import org.apache.shiro.authc.*;
        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("授权");
               return null;
           }
           //认证
           @Override
           protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
               System.out.println("认证");

        String name = "root";
               String password = "123456";

        UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;

        if (!userToken.getUsername().equals(name)){
                   return null;//抛出异常  用户名错误那个异常
               }

        //密码认证,shiro自己做
               return new SimpleAuthenticationInfo("",password,"");
           }
        }

        但是,我们在用户认证这里,真实情况是从数据库中取的,所以,我们接下来去实现一下从数据库中取出数据来实现用户认证

        Shiro整合mybatis

        前期工作

        在前面导入的依赖中,继续添加以下依赖


               <!--  mysql      -->
               <dependency>
                   <groupId>mysql</groupId>
                   <artifactId>mysql-connector-java</artifactId>
               </dependency>
               <!--   log4j     -->
               <dependency>
                   <groupId>log4j</groupId>
                   <artifactId>log4j</artifactId>
                   <version>1.2.17</version>
               </dependency>
               <!--  数据源Druid      -->
               <dependency>
                   <groupId>com.alibaba</groupId>
                   <artifactId>druid</artifactId>
                   <version>1.2.5</version>
               </dependency>
               <!--   引入mybatis     -->
               <dependency>
                   <groupId>org.mybatis.spring.boot</groupId>
                   <artifactId>mybatis-spring-boot-starter</artifactId>
                   <version>2.1.4</version>
               </dependency>
               <!--  lombok      -->
               <dependency>
                   <groupId>org.projectlombok</groupId>
                   <artifactId>lombok</artifactId>
               </dependency>

        导入了mybatis和Druid,就去application.properties配置一下和Druid
        Druid


        spring:
         datasource:
           username: root
           password: 123456
           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

        mybatis


        mybatis:
         type-aliases-package: com.example.pojo
         mapper-locations: classpath:mapper/*.xml

        连接数据库
        编写实体类


        package com.example.pojo;

        import lombok.AllArgsConstructor;
        import lombok.Data;
        import lombok.NoArgsConstructor;

        @Data
        @AllArgsConstructor
        @NoArgsConstructor
        public class User {
               private Integer id;
               private String name;
               private String pwd;
        }

        编写mapper


        package com.example.mapper;

        import com.example.pojo.User;
        import org.apache.ibatis.annotations.Mapper;
        import org.springframework.stereotype.Repository;

        @Repository
        @Mapper
        public interface UserMapper {
           public User getUserByName(String name);
        }

        编写mapper.xml


        <?xml version="1.0" encoding="UTF8" ?>
        <!DOCTYPE mapper
               PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
               "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

        <mapper namespace="com.example.mapper.UserMapper">

        <select id="getUserByName" parameterType="String" resultType="User">
               select * from mybatis.user where name=#{name}
           </select>

        </mapper>

        编写service


        package com.example.service;

        import com.example.pojo.User;

        public interface UserService {
           public User getUserByName(String name);
        }


        package com.example.service;

        import com.example.mapper.UserMapper;
        import com.example.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 getUserByName(String name) {
               return userMapper.getUserByName(name);
           }
        }

        使用数据库中的数据

        修改UserRealm.java即可


        package com.example.config;

        import com.example.pojo.User;
        import com.example.service.UserService;
        import org.apache.shiro.authc.*;
        import org.apache.shiro.authz.AuthorizationInfo;
        import org.apache.shiro.realm.AuthorizingRealm;
        import org.apache.shiro.subject.PrincipalCollection;
        import org.springframework.beans.factory.annotation.Autowired;

        public class UserRealm extends AuthorizingRealm {

        @Autowired
           UserService userService;

        //授权
           @Override
           protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
               System.out.println("授权");
               return null;
           }
           //认证
           @Override
           protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
               System.out.println("认证");

        UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;

        //连接真实的数据库
               User user = userService.getUserByName(userToken.getUsername());

        if (user==null){
                   return null;//抛出异常  用户名错误那个异常
               }

        //密码认证,shiro自己做
               return new SimpleAuthenticationInfo("",user.getPwd(),"");
           }
        }

        认证搞完了,我们再来看看授权

        在ShiroConfig.java文件加入授权,加入这行代码: filterMap.put("/user/add","perms[user:add]");//只有拥有user:add权限的人才能访问add,注意授权的位置在认证前面,不然授权会认证不了;

        Java安全框架——Shiro的使用详解(附springboot整合Shiro的demo)

        运行测试:add页面无法访问

        Java安全框架——Shiro的使用详解(附springboot整合Shiro的demo)

        授权同理:filterMap.put("/user/update","perms[user:update]");//只有拥有user:update权限的人才能访问update

        自定义一个未授权跳转页面

        在ShiroConfig.java文件设置未授权时跳转到unauthorized页面,加入这行代码:
        factoryBean.setUnauthorizedUrl("/unauthorized"); 2. 去Mycontroller写跳转未授权页面


           @RequestMapping("/unauthorized")
           @ResponseBody//懒得写界面,返回一个字符串
           public String unauthorized(){
               return "没有授权,无法访问";
           }

        运行效果:

        Java安全框架——Shiro的使用详解(附springboot整合Shiro的demo)

        从数据库中接受用户的权限,进行判断

        在数据库中添加一个属性perms,相应的实体类也要修改

        Java安全框架——Shiro的使用详解(附springboot整合Shiro的demo)

        Java安全框架——Shiro的使用详解(附springboot整合Shiro的demo)

        修改UserRealm.java


        package com.example.config;

        import com.example.pojo.User;
        import com.example.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.subject.PrincipalCollection;
        import org.apache.shiro.subject.Subject;
        import org.springframework.beans.factory.annotation.Autowired;

        public class UserRealm extends AuthorizingRealm {

        @Autowired
           UserService userService;

        //授权
           @Override
           protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
               System.out.println("授权");
               SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        //没有使用数据库,直接自己设置的用户权限,给每个人都设置了,现实中要从数据库中取
               //info.addStringPermission("user:add");

        //从数据库中得到权限信息
               //获得当前登录的对象
               Subject subject = SecurityUtils.getSubject();
               //拿到User对象,通过getPrincipal()获得
               User currentUser = (User) subject.getPrincipal();

        //设置当前用户的权限
               info.addStringPermission(currentUser.getPerms());

        return info;
           }
           //认证
           @Override
           protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
               System.out.println("认证");

        UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;

        //连接真实的数据库
               User user = userService.getUserByName(userToken.getUsername());

        if (user==null){
                   return null;//抛出异常  用户名错误那个异常
               }

        //密码认证,shiro自己做
               return new SimpleAuthenticationInfo(user,user.getPwd(),"");
           }
        }

        Java安全框架——Shiro的使用详解(附springboot整合Shiro的demo)

        有了授权后,就又出现了一个问题,我们是不是要让用户没有权限的东西,就看不见呢?这时候,就出现了Shiro-thymeleaf整合

        Shiro-thymeleaf整合

        导入整合的依赖


        <!-- https://mvnrepository.com/artifact/com.github.theborakompanioni/thymeleaf-extras-shiro -->
        <dependency>
          <groupId>com.github.theborakompanioni</groupId>
           <artifactId>thymeleaf-extras-shiro</artifactId>
           <version>2.0.0</version>
        </dependency>

        在ShiroConfig整合ShiroDialect


           //整合ShiroDialect: 用来整合 shiro thymeleaf
           @Bean
           public ShiroDialect getShiroDialect(){
               return  new ShiroDialect();
           }

        修改index页面


        <html lang="en" xmlns:th="http://www.thymeleaf.org"
             xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
        <!-- 三个命名空间
        xmlns:th="http://www.thymeleaf.org"
        xmlns:sec="http://www.thymeleaf.org/extras/spring-security"
        xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro"
        -->
        <head>
           <meta charset="UTF-8">
           <title>首页</title>
        </head>
        <body>

        <h1>首页</h1>

        <p th:text="${msg}"></p>

        <!--判断是否有用户登录,如果有就不显示登录按钮-->
        <div th:if="${session.loginUser==null}">
           <a th:href="@{/toLogin}" rel="external nofollow" >登录</a>
        </div>

        <div shiro:hasPermission="user:add">
           <a th:href="@{/user/add}" rel="external nofollow"  rel="external nofollow"  rel="external nofollow" >add</a>
        </div>
        <div shiro:hasPermission="user:update">
           <a th:href="@{/user/update}" rel="external nofollow"  rel="external nofollow"  rel="external nofollow" >update</a>
        </div>

        </body>
        </html>

        判断是否有用户登录


          //这个是整合shiro和thymeleaf用到的,让登录按钮消失的判断
               Subject subject = SecurityUtils.getSubject();
               Session session = subject.getSession();
               session.setAttribute("loginUser", user);

        测试

        来源:https://juejin.cn/post/6949793422483914789

        标签:java,安全框架,springboot,Shiro
        0
        投稿

        猜你喜欢

      • 浅析java修饰符访问权限(动力节点Java学院整理)

        2023-07-13 18:07:34
      • Netty序列化深入理解与使用

        2023-05-24 20:13:07
      • java字符串常用操作方法(查找、截取、分割)

        2023-11-29 03:21:13
      • IntelliJ IDEA2019实现Web项目创建示例

        2023-06-05 00:29:33
      • java 读取excel文件转换成json格式的实例代码

        2023-09-11 13:07:28
      • Android编程实现WebView添加进度条的方法

        2023-07-06 03:16:46
      • Java关键字详解之final static this super的用法

        2022-01-19 09:24:39
      • GSON实现Java对象与JSON格式对象相互转换的完全教程

        2023-11-23 09:23:37
      • java导出生成word的简单方法

        2023-11-23 23:07:13
      • Java 如何优雅的抛出业务异常

        2023-11-24 04:04:34
      • Mybatis使用JSONObject接收数据库查询的方法

        2023-01-17 05:10:43
      • Java中Future和FutureTask的示例详解及使用

        2023-01-29 11:48:42
      • springboot 实战:异常与重定向问题

        2022-03-06 15:44:54
      • SpringBoot中@ConfigurationProperties注解实现配置绑定的三种方法

        2023-03-19 12:36:25
      • Java开发环境配置方法

        2022-05-25 02:02:29
      • Java实现人机猜拳小游戏

        2023-10-07 16:11:37
      • C++右值引用与move和forward函数的使用详解

        2023-07-05 19:27:33
      • MyBatis图文并茂讲解注解开发多对多查询

        2022-10-27 17:05:04
      • Android 在程序运行时申请权限的实例讲解

        2023-08-04 17:35:57
      • 详解SpringCloud mysql实现配置中心

        2023-11-02 05:12:57
      • asp之家 软件编程 m.aspxhome.com