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

    Shiro主要用来进行权限管理。简单的介绍如下:

    一、概念

    Shiro是一个安全框架,可以进行角色、权限管理。

    Shiro主要功能如下:
    Authentication(认证):用户身份识别,通常被称为用户“登录”
    Authorization(授权):访问控制。比如某个用户是否具有某个操作的使用权限。
    Session Management(会话管理):特定于用户的会话管理,甚至在非web 或 EJB 应用程序。
    Cryptography(加密):在对数据源使用加密算法加密的同时,保证易于使用。
    二、主要的类

    1.Subject:当前用户,Subject可以是一个人,也可以是第三方服务
    2.SecurityManager:管理所有Subject,可以配合内部安全组件。

    3.principals:身份,即主体的标识属性,可以是任何东西,如用户名、邮箱等,唯一即可。一个主体可以有多个principals,但只有一个Primary principals,一般是用户名/密码/手机号。
    4.credentials:证明/凭证,即只有主体知道的安全值,如密码/数字证书等。
    最常见的principals和credentials组合就是用户名/密码了。

    5.Realms:用于进行权限信息的验证,需要自己实现。
    6.Realm 本质上是一个特定的安全 DAO:它封装与数据源连接的细节,得到Shiro 所需的相关的数据。
    在配置 Shiro 的时候,你必须指定至少一个Realm 来实现认证(authentication)和/或授权(authorization)。
    我们需要实现Realms的Authentication 和 Authorization。其中 Authentication 是用来验证用户身份,Authorization 是授权访问控制,用于对用户进行的操作授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。

    7.SimpleHash,可以通过特定算法(比如md5)配合盐值salt,对密码进行多次加密。

     三、Shiro配置

    1.Spring集成Shiro一般通过xml配置,SpringBoot集成Shiro一般通过java代码配合@Configuration和@Bean配置。

    2.Shiro的核心通过过滤器Filter实现。Shiro中的Filter是通过URL规则来进行过滤和权限校验,所以我们需要定义一系列关于URL的规则和访问权限。

    3.SpringBoot集成Shiro,我们需要写的主要是两个类,ShiroConfiguration类,还有继承了AuthorizingRealm的Realm类

    ShiroConfiguration类,用来配置Shiro,注入各种Bean。

    包括过滤器(shiroFilter)、安全事务管理器(SecurityManager)、密码凭证(CredentialsMatcher)、aop注解支持(authorizationAttributeSourceAdvisor)等等

    Realm类,包括登陆认证(doGetAuthenticationInfo)、授权认证(doGetAuthorizationInfo)

    四、具体示例如下:

    ShiroConfiguration.java如下:

    package com.example.demo.config;
    
    
    import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
    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.CookieRememberMeManager;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.apache.shiro.web.servlet.Cookie;
    import org.apache.shiro.web.servlet.SimpleCookie;
    import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.DependsOn;
    
    import javax.servlet.Filter;
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    /**
     * Created by lenovo on  三月
     */
    @Configuration
    public class ShiroConfiguration {
    
        @Bean
         public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
             ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
             //设置安全管理器
             shiroFilterFactoryBean.setSecurityManager(securityManager);
             //默认跳转到登陆页面
             shiroFilterFactoryBean.setLoginUrl("/login");
             //登陆成功后的页面
             shiroFilterFactoryBean.setSuccessUrl("/index");
             shiroFilterFactoryBean.setUnauthorizedUrl("/403");
    
             //自定义过滤器
            Map<String,Filter> filterMap=new LinkedHashMap<>();
            shiroFilterFactoryBean.setFilters(filterMap);
            //权限控制map
            Map<String,String> filterChainDefinitionMap=new LinkedHashMap<>();
            // 配置不会被拦截的链接 顺序判断
            filterChainDefinitionMap.put("/static/**", "anon");
            //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
            filterChainDefinitionMap.put("/logout", "logout");
    //        //<!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
    //        //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
    //        filterChainDefinitionMap.put("/**", "anon");
    
             shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
             return shiroFilterFactoryBean;
         }
    
        /**
         * 核心的安全事务管理器
         * @return
         */
         @Bean
        public SecurityManager securityManager(){
             DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager ();
             //设置realm
             securityManager.setRealm( myShiroRealm(  )  );
             securityManager.setRememberMeManager(rememberMeManager());
             return securityManager;
         }
    
    
        /**
         * 身份认证Realm,此处的注入不可以缺少。否则会在UserRealm中注入对象会报空指针.
         * @return
         */
        @Bean
        public UserRealm myShiroRealm(  ){
            UserRealm myShiroRealm = new UserRealm();
            myShiroRealm.setCredentialsMatcher(  hashedCredentialsMatcher() );
            return myShiroRealm;
        }
    
    
    
        /**
         * 哈希密码比较器。在myShiroRealm中作用参数使用
         * 登陆时会比较用户输入的密码,跟数据库密码配合盐值salt解密后是否一致。
         * @return
         */
        @Bean
        public HashedCredentialsMatcher hashedCredentialsMatcher(){
            HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
            hashedCredentialsMatcher.setHashAlgorithmName("md5");//散列算法:这里使用md5算法;
            hashedCredentialsMatcher.setHashIterations(2);//散列的次数,比如散列两次,相当于 md5( md5(""));
            return hashedCredentialsMatcher;
        }
    
    //    //注入缓存
    //    @Bean
    //    public EhCacheManager ehCacheManager(){
    //        System.out.println("ShiroConfiguration.getEhCacheManager()执行");
    //        EhCacheManager cacheManager=new EhCacheManager();
    //        cacheManager.setCacheManagerConfigFile("classpath:config/ehcache-shiro.xml");
    //        return cacheManager;
    //    }
    
        /**
         *  开启shiro aop注解支持.
         *  使用代理方式;所以需要开启代码支持;否则@RequiresRoles等注解无法生效
         * @param securityManager
         * @return
         */
        @Bean
        public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
            AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
            authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
            return authorizationAttributeSourceAdvisor;
        }
    
        /**
         * Shiro生命周期处理器
         * @return
         */
        @Bean
        public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
            return new LifecycleBeanPostProcessor();
        }
    
        /**
         * 自动创建代理
         * @return
         */
        @Bean
        @DependsOn({"lifecycleBeanPostProcessor"})
        public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
            DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
            advisorAutoProxyCreator.setProxyTargetClass(true);
            return advisorAutoProxyCreator;
        }
    }

    Realm类如下:

    package com.example.demo.config;
    
    import com.example.demo.pojo.SysPermission;
    import com.example.demo.pojo.SysUserRole;
    import com.example.demo.pojo.User;
    import com.example.demo.service.UserSerevice;
    import com.example.demo.utils.State;
    import org.apache.log4j.Logger;
    import org.apache.shiro.authc.*;
    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 org.apache.shiro.util.ByteSource;
    
    import javax.annotation.Resource;
    import java.util.ArrayList;
    import java.util.HashSet;
    import java.util.List;
    import java.util.Set;
    
    import static org.apache.coyote.http11.Constants.a;
    
    /**
     * Created by lenovo on  三月
     */
    public class UserRealm extends AuthorizingRealm {
        @Resource(name = "userServiceImp")
        private UserSerevice userService;
    
        private Logger logger=Logger.getLogger(UserRealm.class);
    
        /**
         * 提供用户信息,返回权限信息
         * @param principals
         * @return
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            logger.info("---------------------------->授权认证:");
            SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();
            String userName=(String) principals.getPrimaryPrincipal();
            String userId=userService.findUserIdByName(userName);
            Set<SysUserRole> roleIdSet=userService.findRoleIdByUid( Integer.parseInt(userId) );
            Set<String> roleSet=new HashSet<>();
            Set<Integer>  pemissionIdSet=new HashSet<>();
            Set<String>  pemissionSet=new HashSet<>();
            for(SysUserRole roleInfo : roleIdSet) {
                  int roleId=roleInfo.getRoleId();
                   roleSet.add( userService.findRoleByRoleId( roleId  ) );
                   //将拥有角色的所有权限放进Set里面,也就是求Set集合的并集
                   //由于我这边的数据表设计得不太好,所以提取set集合比较麻烦
                  pemissionIdSet.addAll( userService.findPermissionIdByRoleId(  roleId ));
            }
            for(int permissionId : pemissionIdSet) {
                String permission= userService.findPermissionById( permissionId ).getPermission() ;
                pemissionSet.add(  permission );
            }
             // 将角色名称组成的Set提供给授权info
            authorizationInfo.setRoles( roleSet );
            // 将权限名称组成的Set提供给info
            authorizationInfo.setStringPermissions(pemissionSet);
    
            return authorizationInfo;
        }
    
        /**
         * 提供帐户信息,返回认证信息
         * @param authenticationToken
         * @return
         * @throws AuthenticationException
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            logger.info("---------------------------->登陆验证:");
            String userName=(String)authenticationToken.getPrincipal();
            User user=userService.findUserByName(userName);
            if(user==null) {
                //用户不存在就抛出异常
                throw new UnknownAccountException();
            }
            if( State.LOCKED.equals( user.getState() )  ) {
               //用户被锁定就抛异常
               throw new  LockedAccountException();
            }
            //密码可以通过SimpleHash加密,然后保存进数据库。
            //此处是获取数据库内的账号、密码、盐值,保存到登陆信息info中
            SimpleAuthenticationInfo authenticationInfo=new SimpleAuthenticationInfo(user.getUsername(),
                    user.getPassword(),
                    ByteSource.Util.bytes(user.getSalt())   ,
                    getName());                   //realm name
    
            return authenticationInfo;
        }
    }

    更具体的代码,参见GitHub:

    https://github.com/firefoxer1992/SpringBootProject

    参考博客:

    http://www.ityouknow.com/springboot/2017/06/26/springboot-shiro.html

    盐值及密码的加密,可以参考博客:

    https://blog.csdn.net/qq_31080089/article/details/53715910

  • 相关阅读:
    java1.8--OptionalInt,OptionalDouble,OptionalLong类
    java1.8--Optional类
    java1.8--Null Object模式
    JDK自带的缓存--包装类的缓存
    函数式编程--lambda表达式对比匿名内部类
    java1.8--改进的接口
    Mybatis Generator(定制化)代码生成器
    Mybatis
    git fork
    git原理图解
  • 原文地址:https://www.cnblogs.com/expiator/p/8651798.html
Copyright © 2011-2022 走看看