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

  • 相关阅读:
    什么是webview
    juqery.fn.extend和jquery.extend
    LeetCode
    5. Longest Palindromic Substring
    42. Trapping Rain Water
    11. Container With Most Water
    621. Task Scheduler
    49. Group Anagrams
    739. Daily Temperatures
    3. Longest Substring Without Repeating Characters
  • 原文地址:https://www.cnblogs.com/liconglong/p/11767009.html
Copyright © 2011-2022 走看看