zoukankan      html  css  js  c++  java
  • SpringBoot--集成Shiro

      shiro主要有用户认证和用户授权两个功能

    一、用户认证

    1、导入依赖

            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-spring</artifactId>
                <version>1.4.0</version>
            </dependency>

    2、新增测试页面

      新增测试页面:

        login.html(登陆页面)、index.html(登陆成功页面)、error.html (无权限页面)、add.html(添加页面)、update.html(修改页面)

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
    <head>
        <meta charset="UTF-8">
        <title>登录页面</title>
    </head>
    <body>
    <h1>登录页面</h1>
    <hr/>
    <h3 th:text="${msg}" style="color: red"></h3>
    <form method="post" action="login">
        用户名 :<input type="text" name="username"><br>
        密 码 :<input type="password" name="password"><br>
        <input type="submit" value="登录">
    </form>
    </body>
    </html>
    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
    <head>
        <meta charset="UTF-8">
        <title>测试Thymeleaf</title>
    </head>
    <body>
    <h3 th:text="${test}"></h3>
    <hr/>
    进入用户添加页面:<a href="add">用户添加</a>
    <br>
    进入用户修改页面:<a href="update">用户修改</a>
    </body>
    </html>
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>无权限</title>
    </head>
    <body>
    <h1>无权限页面</h1>
    </body>
    </html>
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>用户添加页面</title>
    </head>
    <body>
    <h1>用户添加页面</h1>
    </body>
    </html>
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>用户修改页面</title>
    </head>
    <body>
    <h1>用户修改页面</h1>
    </body>
    </html>

    3、新增控制类

      新增控制类,包含登陆方法(login)、跳转登陆页面方法(toLogin)、添加用户页面(add)、修改用户页面(update)、不被拦截的方法(hello)

      其中需要注意的就是登陆方法(login),所有登录校验都交给shiro框架处理。

    package com.example.demo.controller;
    
    import com.example.demo.resource.ShiroConfig;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.*;
    import org.apache.shiro.subject.Subject;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.*;
    
    @Controller
    public class LoginController {
    
        private Logger logger = LoggerFactory.getLogger(ShiroConfig.class);
    
        @ResponseBody
        @GetMapping(value = "/hello")
        public String hello(){
            logger.info("不登陆也能访问");
            return "hello不登陆也能访问";
        }
    
        @RequestMapping(value = "/index")
        public String testThymeleaf(Model model) {
            //把数据存入model
            model.addAttribute("test", "测试Thymeleaf");
            //返回test.html
            return "/index";
        }
    
        @RequestMapping(value = "/add")
        public String add(Model model) {
            //把数据存入model
            model.addAttribute("test", "添加用户页面");
            //返回test.html
            return "/user/add";
        }
    
        @RequestMapping(value = "/update")
        public String update(Model model) {
            //把数据存入model
            model.addAttribute("test", "修改用户页面");
            //返回test.html
            return "/user/update";
        }
    
        @RequestMapping(value = "/toLogin")
        public String toLogin(){
            logger.info("跳转登陆页面");
            return "/login";
        }
    
    
    
        @PostMapping(value = "login")
        public String login(String username,String password, Model model){
            logger.info("登陆用户名【{}】,密码【{}】",username,password);
            Subject subject = SecurityUtils.getSubject();
            UsernamePasswordToken token = new UsernamePasswordToken(username,password);
            try{
                subject.login(token);
            }catch (UnknownAccountException e){
                logger.error("对用户[{}]进行登录验证,验证未通过,用户不存在", username);
                token.clear();
                return "login";
            }catch (LockedAccountException lae) {
                logger.error("对用户[{}]进行登录验证,验证未通过,账户已锁定", username);
                token.clear();
                return "login";
            } catch (ExcessiveAttemptsException e) {
                logger.error("对用户[{}]进行登录验证,验证未通过,错误次数过多", username);
                token.clear();
                return "login";
            } catch (AuthenticationException e) {
                logger.error("对用户[{}]进行登录验证,验证未通过,堆栈轨迹如下", username, e);
                token.clear();
                return "login";
            }
            return "/index";
        }
    
    
    }

    4、(重点)创建自己的认证类

      认证类需要继承AuthorizingRealm类,并重写认证和授权)两个方法。

        认证方法(doGetAuthenticationInfo):该方法在登陆时调用,用来做登录校验

        授权方法(doGetAuthorizationInfo):该方法在需要验证权限时调用,用来判断是否拥有权限

      此处先校验登陆问题,因此先只处理认证方法,授权问题详见第二点。在认证方法中,查询了数据库与前台传入的用户名、密码进行校验,此处的查询数据库的一系列文件(service、serviceImpl、mapper、dao、数据库建表等)不再演示。

    package com.example.demo.utils;
    
    import com.example.demo.entity.User;
    import com.example.demo.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.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Configuration;
    
    import java.util.*;
    
    /**
     * 认证
     */
    @Configuration
    public class AuthRealm extends AuthorizingRealm {
    
        Logger logger = LoggerFactory.getLogger(getClass());
    
        @Autowired
        private UserService userService;
    
        /**
         * 只有需要验证权限时才会调用, 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用.在配有缓存的情况下,只加载一次.
         * 如果需要动态权限,但是又不想每次去数据库校验,可以存在ehcache中.自行完善
         * @param principalCollection
         * @return
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            logger.info("用户授权");
            return null;
        }
    
        /**
         * 认证回调函数,登录时调用
         * 首先根据传入的用户名获取User信息;然后如果user为空,那么抛出没找到帐号异常UnknownAccountException;
         * 如果user找到但锁定了抛出锁定异常LockedAccountException;最后生成AuthenticationInfo信息,
         * 交给间接父类AuthenticatingRealm使用CredentialsMatcher进行判断密码是否匹配,
         * 如果不匹配将抛出密码错误异常IncorrectCredentialsException;
         * 另外如果密码重试此处太多将抛出超出重试次数异常ExcessiveAttemptsException;
         * 在组装SimpleAuthenticationInfo信息时, 需要传入:身份信息(用户名)、凭据(密文密码)、盐(username+salt),
         * CredentialsMatcher使用盐加密传入的明文密码和此处的密文密码进行匹配。
         * @param token
         * @return
         * @throws AuthenticationException
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            logger.info("用户认证");
            UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
            User user = userService.selectOneByUsername(usernamePasswordToken.getUsername());
            //2、判断用户名称是否存在
            if (null == user || !user.getUsername().equals(usernamePasswordToken.getUsername())) {
                //用户名称不存在,Shiro底层会抛出UnknowAccountException
                return null;
            }
            //3、判断密码是否正确
            return new SimpleAuthenticationInfo((String)token.getPrincipal(), user.getPassword(), user.getUsername());
        }
    }

    5、(重点)配置shiro配置类

      shiro配置类里面主要有三点:

        (1)获取自定义的认证类

        (2)获取带有认证类(自定义的认证类)的安全管理器(SecurityManager)

        (3)获取带有安全管理器的过滤器

      关于过滤器,主要时配置拦截规则,以及各种情况跳转的页面信息。其中要注意两点,一定要把resource下的所有文件置为无需拦截,否则就会发生虽然方法没有被拦截,但是由于页面被拦截而跳转登陆页面的情况;还有一个就是filterMap的最后一定要要把/**职位需要验证。

    package com.example.demo.resource;
    
    import com.example.demo.utils.AuthRealm;
    import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import java.util.HashMap;
    import java.util.Map;
    
    @Configuration
    public class ShiroConfig {
        private static final Logger logger = LoggerFactory.getLogger(ShiroConfig.class);
    
        @Bean
        public AuthRealm getAuthRealm(){
            return new AuthRealm();
        }
    
        @Bean
        public DefaultWebSecurityManager getDefaultWebSecurityManager(AuthRealm authRealm){
            DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
            defaultWebSecurityManager.setRealm(authRealm);
            return defaultWebSecurityManager;
        }
    
        @Bean
        public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            //设置安全管理器
            shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
    /*        内置Shiro过滤器实现相关拦截功能
                    常用的过滤器有:
                        anon  : 无需认证(登录)可以访问
                        authc : 必须认证才能访问
                        user  : 如果使用rememberMe的功能可以直接访问
                        perms : 该资源必须得到资源访问权限才可以使用
                        role  : 该资源必须得到角色授权才可以使用*/
            Map<String,String> filterMap = new HashMap<>();
            filterMap.put("/resource/**","anon");
            filterMap.put("/install","anon");
            filterMap.put("/hello","anon");
            filterMap.put("/login","anon");
            //filterMap.put("/add","perms[user:add]");
            filterMap.put("/**","authc");
    
            shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
         //未登录跳转登陆页面 shiroFilterFactoryBean.setLoginUrl(
    "/toLogin");
         //认证成功跳转URL shiroFilterFactoryBean.setSuccessUrl(
    "/test");
         //登录后无权限URL shiroFilterFactoryBean.setUnauthorizedUrl(
    "/error"); return shiroFilterFactoryBean; } }

    6、测试

      此处测试分为以下几点:

      

    二、资源授权

      第一点中的第5步中关于shiro配置类的代码中有一行代码是注释掉的(filterMap.put("/add","[user:add]");),这个就是对于资源的授权,这一行的代码就是表明访问add时需要user:add权限,放开该行注释后,再点击添加和修改,可以看到用户修改可以正常跳转,但是新增用户就会跳转到我们设置的无权限页面。

          

      所以就需要对用户进行资源的授权。

    1、导入依赖

            <!-- thymeleaf 扩展 shiro-->
            <dependency>
                <groupId>com.github.theborakompanioni</groupId>
                <artifactId>thymeleaf-extras-shiro</artifactId>
                <version>2.0.0</version>
            </dependency>

    2、前端页面添加shiro标签

      此处引入了shiro标签xmlns:shiro="http://www.w3.org/1999/xhtml",然后在连接处使用了该标签 hiro:hasPermission

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml" xmlns:shiro="http://www.w3.org/1999/xhtml">
    <head>
        <meta charset="UTF-8">
        <title>测试Thymeleaf</title>
    </head>
    <body>
    <h3 th:text="${test}"></h3>
    <hr/>
    <div shiro:hasPermission="user:add">
        进入用户添加页面:<a href="add">用户添加</a>
    </div>
    <br>
    <div shiro:hasPermission="user:update">
        进入用户修改页面:<a href="update">用户修改</a>
    </div>
    </body>
    </html>

    3、修改AuthRealm类中的授权方法

    @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            logger.info("用户授权");
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            Subject subject = SecurityUtils.getSubject();
            String username = (String)subject.getPrincipal();
            User user = userService.selectOneByUsername(username);
            simpleAuthorizationInfo.addStringPermission(user.getPerms());
            return simpleAuthorizationInfo;
        }

    4、数据库调整

      为admin用户添加新增权限,为lcl用户添加修改权限

      

    5、测试

      使用admin用户登陆,则只能看到新增按钮,使用lcl用户登陆,则只能看到update按钮。

                          

      关于springBoot整合Shiro就整合完毕了,其中有几点不合理的地方,为了演示整合,就没有写的那么详细,比如说,一般情况下,会使用用户、角色、权限三张表来做权限处理、会使用缓存来对用户信息进行缓存等。

  • 相关阅读:
    Hosts文件的使用
    【java】关于时间
    【java】定时器
    log4j2的使用
    【springMVC】简单的前后端数据交流
    springMVC简单示例
    442. Find All Duplicates in an Array
    448. Find All Numbers Disappeared in an Array Add to List
    xml scheme 示例解析
    《C#高效编程》读书笔记04-使用Conditional特性而不是#if条件编译
  • 原文地址:https://www.cnblogs.com/liconglong/p/11767009.html
Copyright © 2011-2022 走看看