zoukankan      html  css  js  c++  java
  • Springboot整合Shiro安全框架

       最近在学习Springboot,在这个过程中遇到了很多之前都没有技术知识,学习了一阵子,稍微总结一些。

    ---- Shiro框架

       shiro框架,是一个相对比较简便的安全框架,它可以干净利落地处理身份验证、授权、企业会话管理和加密。

       在此引入网上众多资料,归总shiro大致框架如下:

     

    • Subject:   主体,代表了当前“用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是Subject,如网络爬虫,机器人等;即一个抽象概念;所有Subject都绑定到SecurityManager,与Subject的所有交互都会委托给SecurityManager;可以把Subject认为是一个门面;SecurityManager才是实际的执行者
    • Shiro SecurityManager:   管理所有Subject,SecurityManager 是 Shiro 架构的核心,配合内部安全组件共同组成安全伞.
    • Realm:  用于进行权限信息的验证,我们自己实现。Realm 本质上是一个特定的安全 DAO:它封装与数据源连接的细节,得到Shiro 所需的相关的数据。在配置 Shiro 的时候,你必须指定至少一个Realm 来实现认证(authentication)和/或授权(authorization)

          在主要开发上,我将Shiro的实际应用归结成两大块,其一是关于shiro的一些配置信息,这里称为ShiroConfig文件,由于Apache Shiro 核心通过 Filter 来实现,既然是使用 Filter 一般也就能猜到,是通过 URL 规则来进行过滤和权限校验,所以我们需要定义一系列关于 URL 的规则和访问权限,以及一些Shiro自身具备的一些Bean工具。其二是关于我们自己需要定义的Realm类,这里称为ShiroRealm文件,继承AuthorizingRealm 抽象类,重载 doGetAuthenticationInfo(),重写获取用户信息的方法,重写doGetAuthorizationInfo(),进行角色权限的配置。

    ---?ShiroConfig

    package springbootshiro2.demo.configurer;
    
    import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
    import org.apache.shiro.cache.ehcache.EhCacheManager;
    import org.apache.shiro.codec.Base64;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.session.mgt.SessionManager;
    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.CookieRememberMeManager;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.apache.shiro.web.mgt.WebSecurityManager;
    import org.apache.shiro.web.servlet.SimpleCookie;
    import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    /**
     * 〈一句话功能简述〉<br> 
     * 〈〉
     *
     * @author X450J
     * @create 2019/5/30
     * @since 1.0.0
     */
    @Configuration
    public class ShiroConf {
    
        //注入shiro过滤器
        @Bean("shiroFilterFactoryBean")
        public ShiroFilterFactoryBean shiroFiltr(WebSecurityManager securityManager){
    
          ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
          //设置管理器
          shiroFilterFactoryBean.setSecurityManager(securityManager);
            shiroFilterFactoryBean.setLoginUrl("/login");  // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
            shiroFilterFactoryBean.setSuccessUrl("/index");// 登录成功后要跳转的链接
            shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");//设置无权限跳转页面
            Map<String,String> chains = new LinkedHashMap<>();
            chains.put("/logout","logout");
            chains.put("/login", "anon");//anon表示可以匿名访问
            chains.put("/login/qq", "anon");//anon表示可以匿名访问
            chains.put("/authorize/qq", "anon");//anon表示可以匿名访问
            //对PermissionAction.class 中的url进行权限控制
            chains.put("/user", "roles[user]");//需要user角色才可以访问
            chains.put("/user/per", "perms[user:query]");//需要user角色才可以访问
            chains.put("/admin", "roles[admin]");//需要admin角色才可以访问
            //chains.put("/**", "authc");//表示需要认证,才能访问
            chains.put("/**", "user");//表示需要认证或记a住我都能访问
            shiroFilterFactoryBean.setFilterChainDefinitionMap(chains);
            return shiroFilterFactoryBean;
        }
        //安全管理器
        @Bean
        public WebSecurityManager securityManager(){
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            //设置realm
            securityManager.setRealm(shiroRealm());
            securityManager.setCacheManager(ehCacheManager());
            securityManager.setRememberMeManager(rememberMeManager());
            return securityManager;
        }
    
        //会话管理器
        @Bean
        public SessionManager sessionManager(){
            DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
            sessionManager.setSessionIdUrlRewritingEnabled(true);
            sessionManager.setGlobalSessionTimeout(1 * 60 * 60 * 1000);
            sessionManager.setDeleteInvalidSessions(true);
            sessionManager.setSessionIdCookie(rememberMeCookie());
            return sessionManager;
        }
    
        //Realm,里面需要自己实现认证和授权业务
        @Bean
        public ShiroRealm shiroRealm() {
            ShiroRealm shiroRealm = new ShiroRealm();
            shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
            return shiroRealm;
        }
    
        //缓存管理
        @Bean
        public EhCacheManager ehCacheManager(){
            EhCacheManager ehCacheManager = new EhCacheManager();
            ehCacheManager.setCacheManagerConfigFile("classpath:ehcache.xml");
            return  ehCacheManager;
        }
    
        //密码管理
        @Bean
        public HashedCredentialsMatcher hashedCredentialsMatcher(){
            HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
            credentialsMatcher.setHashAlgorithmName("md5");
            credentialsMatcher.setHashIterations(2);   //加密两次
            credentialsMatcher.setStoredCredentialsHexEncoded(true); //启用十六进制
            return credentialsMatcher;
        }
        //cookie管理
        @Bean
        public SimpleCookie rememberMeCookie() {
            SimpleCookie cookie = new SimpleCookie("rememberMe");
            cookie.setHttpOnly(true);
            cookie.setMaxAge(1 * 60 * 60);
            return cookie;
        }
    
        //管理shiro的生命周期
        @Bean(name = "lifecycleBeanPostProcessor")
        public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
            return new LifecycleBeanPostProcessor();
        }
    
        //记住我
        @Bean
        public CookieRememberMeManager rememberMeManager() {
            CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
            cookieRememberMeManager.setCookie(rememberMeCookie());
            //不是所有的base64编码都可以用,长度过大过小都不行
            cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
            return cookieRememberMeManager;
        }
    
        //开启shiro注解权限控制
        @Bean
        public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
            AuthorizationAttributeSourceAdvisor attributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
            attributeSourceAdvisor.setSecurityManager(securityManager);
            return attributeSourcteAdvisor;
        }
    
    }
    1. LifecycleBeanPostProcessor:它管理Shiro的生命周期。
    2. ShiroFilterFactoryBean:采用了工厂设计模式,负责引入SecurityManager、登录的页面url设置、登录后要跳转的url设置、未授权的页面的url设置、拦截器链的设置。
    3. SecurityManager:Shiro的核心,认证器、缓存器、Realm数据源都在这里配置注入
    4. HashedCredentialsMatcher:Shiro自带密码器,这里用了MD5加密算法,并且还将其注入到自定义的ShiroRealm之中。
    5. CacheManager: 缓存管理器,这里引入了Ehcache框架来实现缓存,接着再一起注入安全管理器(SecurityManager)
    6. AuthorizationAttributeSourceAdvisor: 配置生效后,可以开启shiro的注解权限控制
    7. CookieRememberMeManager: 顾名思义,负责实现RememberMe功能的,使得浏览器请求页面时可以通过cookies判断用户而无需登录
    8. SessionManager: 关于session会话的一些管理

    ---?ShiroRealm

    package springbootshiro2.demo.configurer;
    
    import org.apache.shiro.authc.*;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.crypto.hash.SimpleHash;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.apache.shiro.util.ByteSource;
    import springbootshiro2.demo.model.User;
    
    /**
     * 〈一句话功能简述〉<br> 
     * 〈自定义Realm〉
     *
     * @author X450J
     * @create 2019/5/30
     * @since 1.0.0
     */
    public class ShiroRealm extends AuthorizingRealm {
    
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            //给当前角色授权
            SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
    
            User user = (User) principalCollection.getPrimaryPrincipal();
            //在这里 实际开发应该从数据库进行获取
            if (user.getUsername().equals("xslde")){
                //设置该用户拥有user角色
                authorizationInfo.addRole("user");
                //设置该用户拥有query权限
                authorizationInfo.addStringPermission("user:query");
            }
    
            if(user.getUsername().equals("admin")){
                //admin用户拥有admin、user角色
                authorizationInfo.addRole("admin");
                authorizationInfo.addRole("user");
                //设置该用户拥有query权限
                authorizationInfo.addStringPermission("user:query");
            }
            return authorizationInfo;
        }
    
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            //进行用户信息获取
            String username = (String)token.getPrincipal();
            //开发中,这里都是去数据库查询
            if (!"xslde".equals(username)&&!"test".equals(username)&&!"xslde.com".equals(username)&&!"admin".equals(username)){
                throw new UnknownAccountException("用户不存在!");
            }
            User user = null;
            if ("xslde".equals(username)){
                user = new User();
                user.setUsername("xslde");
                user.setPassword("0caf568dbf30f5c33a13c56b869259fc");
                user.setSalt("abcd");
                user.setAvailable(1);
            }
            if ("admin".equals(username)){
                user = new User();
                user.setUsername("admin");
                user.setPassword("0caf568dbf30f5c33a13c56b869259fc");
                user.setSalt("abcd");
                user.setAvailable(1);
            }
            if ("test".equals(username)){
                user = new User();
                user.setUsername("test");
                user.setPassword("0caf568dbf30f5c33a13c56b869259fc");
                user.setSalt("abcd");
                user.setAvailable(0);
            }
    
            if (user.getAvailable()!=1){
                throw  new LockedAccountException("账户已被锁定");
            }
            return  new SimpleAuthenticationInfo(user, user.getPassword(), ByteSource.Util.bytes(user.getSalt()), getName());
        }
    
       //生成一个加盐密码
        public static void main(String[] args){
            //加密类型
            String hashAlgorithmName = "md5";
            //迭代次数
            Integer count = 2;
            String password = "123456";
            String salt = "abcd";
            String s = new SimpleHash(hashAlgorithmName,password,salt,count).toHex();
            System.out.println(s);
    
        }
    }

    小结两点:第一是理清数据之间关系,即一个用户可以拥有多个角色,而一个角色可以具备多种权限

                     第二是理解关于盐的事情,因为这里用的密码虽然已经通过MD5算法进行加密,但是假如别人是知道这个系统用的是这个加密的算法,那么很容易就通过对应的密码方式进 行破解,所以引入盐的概念,即生活中一道相同的菜在不同人的手里所下的盐的量都会有所区别,借助这个道理,因为我们上面已经在Realm中配置过加密工具了,所以这里不加盐值的话会默认使用"MD5"算法(上面配置加密工具时设置)进行密码加密,如果加了盐值,在算法加密之后会再进行盐值加密过程。

     之后在controller层中,进行如下的配置

    package springbootshiro2.demo.action;
    
    import org.apache.shiro.authz.annotation.RequiresPermissions;
    import org.apache.shiro.authz.annotation.RequiresRoles;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.GetMapping;
    
    import java.util.Map;
    
    /**
     * 〈一句话功能简述〉<br> 
     * 〈〉
     *
     * @author X450J
     * @create 2019/5/30
     * @since 1.0.0
     */
    @Controller
    public class PermissionAction {
    
        @GetMapping("/unauthorized")
        public String unauthorized(){
            return "unauthorized.html";
        }
    
        //拥有user角色才可访问
        @GetMapping("/user")
        public String user(Map<String,String> code){
            code.put("msg","拥有user角色");
            return "user.html";
        }
    
        //拥有user角色才可访问和user:query权限才可访问
        @GetMapping("/user/per")
        public String userPer(Map<String,String> mode){
            mode.put("msg","拥有user角色和user:query权限");
            return "user.html";
        }
    
        //拥有admin角色才可访问
        @GetMapping("/admin")
        public String admin(Map<String,String> mode){
            mode.put("msg","拥有admin角色");
            return "admin.html";
        }
    
        //通过注解控制授权
        @RequiresRoles({"admin"})
        @GetMapping("/abc")
        public String abc(Map<String,String> mode){
            mode.put("msg","拥有admin角色,并且是通过注解控制");
            return "admin.html";
        }
    
        //通过注解控制权限
        @RequiresPermissions({"user:query"})
        @GetMapping("/abcd")
        public String abcd(Map<String,String> mode){
            mode.put("msg","拥有user:query权限,并且是通过注解控制");
            return "user.html";
        }
    }

     主要代码

    参考文章:Spring Boot 整合 Shiro-登录认证和权限管理

                    (SpringBoot)Shiro安全框架深入解析

  • 相关阅读:
    Redis进阶实践之一VMWare Pro虚拟机安装和Linux系统的安装
    HTTP常见面试题
    HBase WAL原理学习
    HBase TableExistsException: hbase:namespace
    HBase常用操作之namespace
    Hbase原理、基本概念、基本架构
    hbase 修复 hbase hbck
    hbase数据备份或者容灾方案
    Hbase 日常运维
    HBase shell scan 模糊查询
  • 原文地址:https://www.cnblogs.com/liangyueyuan/p/10956037.html
Copyright © 2011-2022 走看看