• SpringBoot2.0集成Shiro


    1、shiro的三个核心概念:
      1)Subject:代表当前正在执行操作的用户,但Subject代表的可以是人,也可以是任何第三方系统帐号。当然每个subject实例都会被绑定到SercurityManger上。
      2)SecurityManger:SecurityManager是Shiro核心,主要协调Shiro内部的各种安全组件,这个我们不需要太关注,只需要知道可以设置自定的Realm。
      3)Realm:用户数据和Shiro数据交互的桥梁。比如需要用户身份认证、权限认证。都是需要通过Realm来读取数据。

    2、springboot中集成shiro相对简单,只需要两个类:一个是ShiroConfig类,一个是自定义Realm类。

      1)ShiroConfig类:shiro的一些配置,相对于之前的xml配置。包括:ShiroFilter的配置,密码加密的算法,支持注解的配置等功能。

      2)自定义Realm类:继承AuthorizingRealm。并且重写父类中的doGetAuthorizationInfo(权限认证)、doGetAuthenticationInfo(身份认证)这两个方法。

    3、demo

      项目结构:

      

      依赖:

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

      ShiroConfig

    package com.oy;
    
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.spring.LifecycleBeanPostProcessor;
    import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
    import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.DependsOn;
    
    /**
     * @author oy
     * @date 2019年8月10日 下午4:50:55
     * @version 1.0.0
     */
    @Configuration
    public class ShiroConfig {
    
        /************************* shiroFilter配置 start *************************/
        @Bean(name = "shiroFilter")
        public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            shiroFilterFactoryBean.setSecurityManager(securityManager);
    
            shiroFilterFactoryBean.setLoginUrl("/login");
            shiroFilterFactoryBean.setUnauthorizedUrl("/unauth");
    
            Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
            // authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问
            filterChainDefinitionMap.put("/webjars/**", "anon");
            filterChainDefinitionMap.put("/login", "anon");
            filterChainDefinitionMap.put("/user/login", "anon");
            filterChainDefinitionMap.put("/", "anon");
            filterChainDefinitionMap.put("/front/**", "anon");
            filterChainDefinitionMap.put("/api/**", "anon");
    
            filterChainDefinitionMap.put("/admin/**", "authc");
            filterChainDefinitionMap.put("/user/**", "authc");
    
            // 剩余的都需要认证
            // 这行代码必须放在所有权限设置的最后,不然会导致所有url都被拦截
            filterChainDefinitionMap.put("/**", "authc");
            shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
            return shiroFilterFactoryBean;
        }
    
        @Bean
        public SecurityManager securityManager() {
            DefaultWebSecurityManager defaultSecurityManager = new DefaultWebSecurityManager();
            defaultSecurityManager.setRealm(myRealm());
            return defaultSecurityManager;
        }
    
        @Bean
        public MyRealm myRealm() {
            MyRealm myRealm = new MyRealm();
            return myRealm;
        }
        /************************* shiroFilter配置 end   *************************/
        
        
        /************************* 开启shiro注解配置 start *************************/
        @Bean
        public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
            return new LifecycleBeanPostProcessor();
        }
    
        /**
         * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),
         * 需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证.
         * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
         */
        @Bean
        @DependsOn({ "lifecycleBeanPostProcessor" })
        public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
            DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
            advisorAutoProxyCreator.setProxyTargetClass(true);
            return advisorAutoProxyCreator;
        }
    
        @Bean
        public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
            AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
            authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
            return authorizationAttributeSourceAdvisor;
        }
        /************************* 开启shiro注解配置 end   *************************/
    }

      MyRealm

    package com.oy;
    
    import java.util.HashSet;
    import java.util.Set;
    
    import javax.annotation.Resource;
    
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authc.SimpleAuthenticationInfo;
    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 com.oy.model.User;
    import com.oy.service.UserService;
    
    public class MyRealm extends AuthorizingRealm {
    
        @Resource
        private UserService userService;
    
        /**
         * 认证:身份认证
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            String username = (String) token.getPrincipal();
            User user = userService.getUserByUsername(username);
            if (user != null) {
                AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());
                return authcInfo;
            } else {
                return null;
            }
        }
    
        /**
         * 授权:权限认证
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
            //String userName = (String) principals.getPrimaryPrincipal();
            //authorizationInfo.setRoles(userService.getRoles(userName));
            //authorizationInfo.setStringPermissions(userService.getPermissions(userName));
            
            Set<String> stringSet = new HashSet<>();
            stringSet.add("user:view");
            stringSet.add("user:edit");
            authorizationInfo.setStringPermissions(stringSet);
            return authorizationInfo;
        }
    }

      

      IndexController

    package com.oy.controller;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.ExcessiveAttemptsException;
    import org.apache.shiro.authc.IncorrectCredentialsException;
    import org.apache.shiro.authc.LockedAccountException;
    import org.apache.shiro.authc.UnknownAccountException;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.authz.annotation.RequiresPermissions;
    import org.apache.shiro.subject.Subject;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import com.oy.util.CryptographyUtil;
    
    /**
     * @author oy
     * @date 2019年8月10日 下午6:21:58
     * @version 1.0.0
     */
    @Controller
    public class IndexController {
    
        @RequestMapping("/login")
        public String login() {
            return "login.html";
        }
    
        @RequestMapping("/user/login")
        @ResponseBody
        public String doLogin(@RequestParam(value = "username", required = false) String username,
                @RequestParam(value = "password", required = false) String password) {
    
            System.out.println("username=" + username + ", password=" + password);
            // 从SecurityUtils里边创建一个 subject
            Subject subject = SecurityUtils.getSubject();
            // 在认证提交前准备 token(令牌)
            //UsernamePasswordToken token = new UsernamePasswordToken(username, password);
            UsernamePasswordToken token = new UsernamePasswordToken(username, CryptographyUtil.md5(password, "abc123"));
            // 执行认证登陆
            try {
                subject.login(token);
            } catch (UnknownAccountException uae) {
                return "未知账户";
            } catch (IncorrectCredentialsException ice) {
                return "密码不正确";
            } catch (LockedAccountException lae) {
                return "账户已锁定";
            } catch (ExcessiveAttemptsException eae) {
                return "用户名或密码错误次数过多";
            } catch (AuthenticationException ae) {
                return "用户名或密码不正确!";
            }
            
            if (subject.isAuthenticated()) {
                return "登录成功";
            } else {
                token.clear();
                return "登录失败";
            }
        }
        
        @RequiresPermissions("user:view")
        @GetMapping("/user")
        @ResponseBody
        public String userList() {
            return "user list";
        }
        
        @RequiresPermissions("book:view")
        @GetMapping("/book")
        @ResponseBody
        public String bookList() {
            return "book list";
        }
    
        @RequestMapping("/unauth")
        public String unauth() {
            return "unauth.html";
        }
    }

      login.html

    <body>
        <h2>login页面</h2>
        <form action="/user/login" method="post">
            username: <input type="text" name="username" value=""/></br></br>
            password: <input type="text" name="password" value=""/></br>
            <input type="submit" value="submit"/>
        </form>
    </body>

      CryptographyUtil

    package com.oy.util;
    
    import org.apache.shiro.codec.Base64;
    import org.apache.shiro.crypto.hash.Md5Hash;
    
    public class CryptographyUtil {
    
        /**
         * base64加密
         * @param str
         * @return
         */
        public static String encBase64(String str) {
            return Base64.encodeToString(str.getBytes());
        }
    
        /**
         * base64解密
         * @param str
         * @return
         */
        public static String decBase64(String str) {
            return Base64.decodeToString(str);
        }
    
        /**
         * Md5加密
         * @param str
         * @param salt
         * @return
         */
        public static String md5(String str, String salt) {
            return new Md5Hash(str, salt).toString();
        }
    
        public static void main(String[] args) {
            String password = "123456";
            System.out.println("Base64加密:" + CryptographyUtil.encBase64(password));
            System.out.println("Base64解密:" + CryptographyUtil.decBase64(CryptographyUtil.encBase64(password)));
    
            // a6f70dedd698be90addd35abe38d3876
            System.out.println("Md5加密:" + CryptographyUtil.md5(password, "abc123"));
        }
    }

     参考资料:

      1)SpringBoot2.0集成Shiro

      2)SpringBoot搭建基于Apache Shiro的权限管理功能

      3)shiro无权限,不跳转到指定页面。setUnauthorizedUrl无效

      4)setUnauthorizedUrl("/403")不起作用,不能设置没有权限的跳转页面

      5)shiro的@RequiresPermissions不生效和无权限跳异常而不是shiro指定的无权页面

  • 相关阅读:
    思维导图github地址
    python操作mongodb根据_id查询数据的实现方法
    如何让nginx显示文件夹目录
    Scrapy爬虫返回302重定向问题解决方法
    K8s
    Dockerfile文件详解
    k8s简介
    mongodb存储过程
    存储过程详解
    Docker 镜像加速
  • 原文地址:https://www.cnblogs.com/xy-ouyang/p/11333072.html
Copyright © 2020-2023  润新知