目录
  • 1.Shito简介
    • 1.1 什么是shiro
    • 1.2 有哪些功能
  • 2.QuickStart
    • 3.SpringBoot中集成
      • 1.导入shiro相关依赖
      • 2.自定义UserRealm
      • 3.定义shiroConfig
      • 4.新建页面进行测试

    1.Shito简介

    1.1 什么是shiro

    Apache Shiro是一个java安全(权限)框架

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

    shiro可以完成,认证,授权,加密,会话管理,web集成,缓存等。

    下载地址:http://shiro.apache.org/

    SpringBoot整合Shiro的方法详解

    1.2 有哪些功能

    SpringBoot整合Shiro的方法详解

    2.QuickStart

    Git下载地址

    官网下载的可能慢一些

    首先新建一个普通的maven项目,然后在项目中导入新的maven项目模块,结构如下:

    SpringBoot整合Shiro的方法详解

    然后开始创建我们需要的文件,这些文件都可以在官网下载的文件中可以找到:

    shiro.ini:

    # Users and their assigned roles
    #
    # Each line conforms to the format defined in the
    # org.apache.shiro.realm.text.TextConfigurationRealm#setUserDefinitions JavaDoc
    # -----------------------------------------------------------------------------
    [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
    

    导入相关依赖 pom.xml,官网未给出详细的依赖,具体的参考给出的git下载的文件,然后做了一些简单的修改。

    <?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <parent>
            <artifactId>Shiro</artifactId>
            <groupId>com.nuist</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <modelVersion>4.0.0</modelVersion>
    
        <artifactId>hello-shiro</artifactId>
    
        <properties>
            <maven.compiler.source>8</maven.compiler.source>
            <maven.compiler.target>8</maven.compiler.target>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-core</artifactId>
                <version>1.4.1</version>
            </dependency>
    
            <!-- configure logging -->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>jcl-over-slf4j</artifactId>
                <version>1.7.21</version>
            </dependency>
    
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-log4j12</artifactId>
                <version>1.7.12</version>
            </dependency>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.17</version>
            </dependency>
    
        </dependencies>
    
    </project>
    

    log4j.prop[erties:

    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
    

    最重要的配置文件:

    QuickStart:

    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.*;
    import org.apache.shiro.mgt.DefaultSecurityManager;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.realm.text.IniRealm;
    import org.apache.shiro.session.Session;
    import org.apache.shiro.subject.Subject;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import javax.naming.InitialContext;
    
    
    public class Quickstart {
    
        private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);
    
    
        public static void main(String[] args) {
            // 旧方法,由于shiro更新无法正常使用
            // Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
            // SecurityManager securityManager = factory.getInstance();
    
    
            // 新方法 shiro更新问题 解决正常运行
            DefaultSecurityManager securityManager = new DefaultSecurityManager();
            IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
            securityManager.setRealm(iniRealm);
    
            SecurityUtils.setSecurityManager(securityManager);
    
            // Now that a simple Shiro environment is set up, let's see what you can do:
    
            // 获取当前用户对象
            Subject currentUser = SecurityUtils.getSubject();
    
            // 通过当前用户拿到session
            Session session = currentUser.getSession();
            session.setAttribute("someKey", "aValue");
            String value = (String) session.getAttribute("someKey");
            if (value.equals("aValue")) {
                log.info("subject =>! [" + value + "]");
            }
    
            // 判断当前用户是否被认证
            if (!currentUser.isAuthenticated()) {
                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.");
            }
    
            // 测试一个简单的权限
            // 粗粒度的一个权限限制
            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!");
            }
    
            //注销功能
            currentUser.logout();
    
            System.exit(0);
        }
    }
    

    以上主要的几个关键部分代码:

    对象初始化部分

    // 新方法 shiro更新问题 解决正常运行
    DefaultSecurityManager securityManager = new DefaultSecurityManager();
    IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
    securityManager.setRealm(iniRealm);
    
    SecurityUtils.setSecurityManager(securityManager);
    

    获取当前用户

    Subject currentUser = SecurityUtils.getSubject();
    

    判断用户是否被认证

    !currentUser.isAuthenticated()
    

    判断用户是否具有什么角色

    currentUser.hasRole("schwartz")
    

    判断用户是否拥有权限

    currentUser.isPermitted("lightsaber:wield")
    

    注销

    currentUser.logout();
    

    此时我们运行启动,项目如下,那么一个简单的shiro quickStart 就已经启动好了。

    SpringBoot整合Shiro的方法详解

    注意

    shir最重要的三个部分:

    • subject 用户
    • SecurityManager 管理所有的用户
    • Realm 连接数据

    3.SpringBoot中集成

    1.导入shiro相关依赖

        <!--
        subject  用户
        SecurityManager 管理所有用户
        Realm 连接数据
        -->
        <!--shiro与springboot-->
        <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>1.6.0</version>
        </dependency>
    

    2.自定义UserRealm

    package com.nuist.shirospringboot.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;
    
    /**
     * @author liuhuanhuan
     * @version 1.0
     * @date 2022/5/8 17:59
     * @Description
     */
    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;
        }
    }
    

    3.定义shiroConfig

    package com.nuist.shirospringboot.config;
    
    import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    /**
     * @author liuhuanhuan
     * @version 1.0
     * @date 2022/5/8 17:58
     * @Description
     */
    @Configuration
    public class ShiroConfig {
        // shiroFilterConfiguere
    
        @Bean
        public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager securityManager) {
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            // 设置用户管理器
            shiroFilterFactoryBean.setSecurityManager(securityManager);
            return shiroFilterFactoryBean;
        }
        // defaultWebSecurity
        // 通过@Qualifier 是USerRealm进行绑定
        @Bean
        public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userReaml") UserRealm userReaml) {
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            // 关联Realm
            securityManager.setRealm(userReaml);
            return securityManager;
        }
    
    
        // 创建realm对象  需要去进行自定义,这样就可以交给spring去进行托管了
        @Bean
        public UserRealm userReaml(){
            return new UserRealm();
        }
    }
    

    4.新建页面进行测试

    package com.nuist.shirospringboot.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    /**
     * @author liuhuanhuan
     * @version 1.0
     * @date 2022/5/8 17:52
     * @Description
     */
    @Controller
    public class Mycontrollrt
    {
        @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";
        }
    }
    

    index.html

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    
    <h1>首页</h1>
    <p th:text="${msg}"></p>
    <hr>
    <a th:href="https://www.freexyz.cn/dev/@{/user/add}">add </a>  |  <a th:href="https://www.freexyz.cn/dev/@{/user/update}">update</a>
    </body>
    </html>
    

    user/add.html

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

    user/update.html

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

    此时一个简单的项目就搭建起来了,然后开始shiro的认证授权的操作。

    我们只需要在配置中添加如下代码:

            Map<String,String> filterMap = new LinkedHashMap<>();
    //        filterMap.put("/user/add","authc");
    //        filterMap.put("/user/update","authc");
            filterMap.put("/user/*","authc");
            shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
    
            shiroFilterFactoryBean.setLoginUrl("/toLogin");
    

    此时就对我们所有的页面请求进行了拦截,然后转发到login的页面

    login.html:

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

    此时在页面进行点击的时候,我们就无法正常进入页面,只能进入到我们的登录页面

    进行登录验证的拦截,只有输入正确的账号密码才能够进入:

    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); // 执行登录的方法,有异常进行处理
                return "index";
            } catch (UnknownAccountException e){
                model.addAttribute("msg","用户名错误");
                return "login";
            } catch (IncorrectCredentialsException e) { // 密码不存在
                model.addAttribute("msg","密码错误");
                return "login";
            }
        }
    

    然后修改login页面的代码

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

    然后在我们的UserRealm

    中doGetAuthenticationInfo方法中新增代码

        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            System.out.println("执行了-》认证doGetAuthenticationInfo");
            String name = "root";
            String password = "123456";
            UsernamePasswordToken authenticationToken1 = (UsernamePasswordToken) authenticationToken;
            if (!authenticationToken1.getUsername().equals(name)) {
                return null;   // 抛出异常
            }
            // 密码认证  shiro去做
            return new SimpleAuthenticationInfo("",password,"");
        }
    

    此时我们通过UsernamePasswordToken 获取我们封装好的账号和密码,但是我们只需要进行账号的认证,密码的认证交给我们的shiro去做就可以了。

    具体的用户授权,我们可以进行连接数据库的设置,但是我为了偷懒,我就不去手动创建与数据库的链接啦。

    下面我们来进行页面的授权操作

    我们想要进行用户的授权操作

    我们需要在shiroConfig中新增如下代码:

    filterMap.put("/user/add","perms[user:add]");
    

    此时代表如果用户拥有user:add操作的话,可以显示,如果没有的话就不能正常显示

    我们可以自定义一个页面来用于返回信息的显示:

     @RequestMapping("/noauth")    
     @ResponseBody                 
     public String noauth() {      
         return "未经过授权无法进行访问";     
     }                             
    

    当用户没有add权限的时候,我们就提示无法显示:

    此时我们就完成了单个的用户授权的操作。此时我们再去进行具体的页面操作

    shiro与thymeleaf的结合:

    需要将我们进行验证的页面进行如下操作:

    <!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</title>
    </head>
    <body>
    
    <h1>首页</h1>
    <p>
        <a th:href="https://www.freexyz.cn/dev/@{/toLogin}">登录</a>
    </p>
    
    <p th:text="${msg}"></p>
    <hr>
    <div shiro:hasPermission="user:add">
        <a th:href="https://www.freexyz.cn/dev/@{/user/add}">add </a>
    </div>
     |
    <div shiro:hasPermission="user:update">
        <a th:href="https://www.freexyz.cn/dev/@{/user/update}">update</a>
    </div>
    </body>
    </html>
    

    此时应用的方式和springsecurity的方式基本一致。

    然后在我们的授权页面进行操作如下:

    UserRealm中修改doGetAuthorizationInfo方法,如下:

        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            System.out.println("执行了=》授权doGetAuthorizationInfo方法");
            SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
    
            // 新增授权页面
            authorizationInfo.addStringPermission("user:add");
    
            // 拿到当前对象 ,然后通过对象中的授权方式进行判断
    
            return authorizationInfo;
        }
    

    此时我们赋予用户只有add的权限,那么按理说在页面中是无法显示update的按钮,那么我们进行测试下,是否可以正常使用:

    SpringBoot整合Shiro的方法详解

    此时认证授权部分已经成功啦,以上就是我们进行的一个小小的demo,更深入的学习,后续继续更新。

    git源码地址

    声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。