• shiro入门


    参考: https://www.jianshu.com/p/7f724bec3dc3

    1.引入maven依赖

    <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    
            <!-- shiro -->
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-spring</artifactId>
                <version>1.5.3</version>
            </dependency>
    
            <!-- Shiro使用EhCache缓存框架 -->
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-ehcache</artifactId>
                <version>1.5.3</version>
            </dependency>
    
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
    
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>5.7.15</version>
            </dependency>
    
        </dependencies>
    

    2.编写自定义Realm类继承AuthorizingRealm

    package com.yxg.erp.config.shiro;
    
    
    import com.yxg.erp.common.shiro.Permissions;
    import com.yxg.erp.module.bean.Role;
    import com.yxg.erp.module.bean.User;
    import com.yxg.erp.module.service.LoginService;
    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 org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.util.StringUtils;
    
    public class CustomRealm extends AuthorizingRealm {
    
        @Autowired
        private LoginService loginService;
    
        /**
         * @MethodName doGetAuthorizationInfo
         * @Description 权限配置类
         * @Param [principalCollection]
         * @Return AuthorizationInfo
         * @Author WangShiLin
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            //获取登录用户名
            String name = (String) principalCollection.getPrimaryPrincipal();
            //查询用户名称
            User user = loginService.getUserByName(name);
            //添加角色和权限
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            for (Role role : user.getRoles()) {
                //添加角色
                simpleAuthorizationInfo.addRole(role.getRoleName());
                //添加权限
                for (Permissions permissions : role.getPermissions()) {
                    simpleAuthorizationInfo.addStringPermission(permissions.getPermissionsName());
                }
            }
            return simpleAuthorizationInfo;
        }
    
        /**
         * @MethodName doGetAuthenticationInfo
         * @Description 认证配置类
         * @Param [authenticationToken]
         * @Return AuthenticationInfo
         * @Author WangShiLin
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            if (StringUtils.isEmpty(authenticationToken.getPrincipal())) {
                return null;
            }
            //获取用户信息
            String name = authenticationToken.getPrincipal().toString();
            User user = loginService.getUserByName(name);
            if (user == null) {
                //这里返回后会报出对应异常
                return null;
            } else {
                //这里验证authenticationToken和simpleAuthenticationInfo的信息
                SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(name, user.getPassword().toString(), getName());
                return simpleAuthenticationInfo;
            }
        }
    }
    

    3.注入Spring相关配置

    package com.yxg.erp.config.shiro;
    
    import cn.hutool.core.io.IoUtil;
    import cn.hutool.core.util.ObjectUtil;
    import org.apache.shiro.cache.ehcache.EhCacheManager;
    import org.apache.shiro.config.ConfigurationException;
    import org.apache.shiro.io.ResourceUtils;
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
    import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.apache.tomcat.util.http.fileupload.IOUtils;
    import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
    import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    import java.io.ByteArrayInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.util.HashMap;
    import java.util.Map;
    
    @Configuration
    public class ShiroConfig {
        
        @Bean
        @ConditionalOnMissingBean
        public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
            DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator();
            defaultAAP.setProxyTargetClass(true);
            return defaultAAP;
        }
    
        /**
         * 缓存管理器 使用Ehcache实现
         */
        @Bean
        public EhCacheManager getEhCacheManager()
        {
            net.sf.ehcache.CacheManager cacheManager = net.sf.ehcache.CacheManager.getCacheManager("ruoyi");
            EhCacheManager em = new EhCacheManager();
            if (ObjectUtil.isEmpty(cacheManager))
            {
                em.setCacheManager(new net.sf.ehcache.CacheManager(getCacheManagerConfigFileInputStream()));
                return em;
            }
            else
            {
                em.setCacheManager(cacheManager);
                return em;
            }
        }
    
        /**
         * 返回配置文件流 避免ehcache配置文件一直被占用,无法完全销毁项目重新部署
         */
        protected InputStream getCacheManagerConfigFileInputStream()
        {
            String configFile = "classpath:ehcache/ehcache-shiro.xml";
            InputStream inputStream = null;
            try
            {
                inputStream = ResourceUtils.getInputStreamForPath(configFile);
                byte[] b = IoUtil.readBytes(inputStream,true);
                InputStream in = new ByteArrayInputStream(b);
                return in;
            }
            catch (IOException e)
            {
                throw new ConfigurationException(
                        "Unable to obtain input stream for cacheManagerConfigFile [" + configFile + "]", e);
            }
            finally
            {
                IOUtils.closeQuietly(inputStream);
            }
        }
    
        //将自己的验证方式加入容器
        @Bean
        public CustomRealm myShiroRealm(EhCacheManager cacheManager) {
            CustomRealm customRealm = new CustomRealm();
            customRealm.setCacheManager(cacheManager);
            return customRealm;
        }
    
        //权限管理,配置主要是Realm的管理认证
        @Bean
        public SecurityManager securityManager(CustomRealm customRealm) {
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            securityManager.setRealm(customRealm);
            return securityManager;
        }
    
        //Filter工厂,设置对应的过滤条件和跳转条件
        @Bean
        public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            shiroFilterFactoryBean.setSecurityManager(securityManager);
            Map<String, String> map = new HashMap<>();
            //登出
            map.put("/logout", "logout");
            //对所有用户认证
            map.put("/**", "authc");
            //登录
            shiroFilterFactoryBean.setLoginUrl("/login");
            //首页
            shiroFilterFactoryBean.setSuccessUrl("/index");
            //错误页面,认证不通过跳转
            shiroFilterFactoryBean.setUnauthorizedUrl("/error");
            shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
            return shiroFilterFactoryBean;
        }
    
      
        @Bean
        public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
            AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
            authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
            return authorizationAttributeSourceAdvisor;
        }
    
    
    }

    原作者gitee代码地址: https://gitee.com/wsl6/shiro-demo

    原理分析

    1.自定义CustomRealm类继承AuthorizingRealm类

    将CustomRealm注入Spring容器中

    2.注入SecurityManager时将自定义CustomRealm设置进去

    3.用户在登录时

    调用Subject.loin(token); //token对象保存了登录名和密码

    经过ModularRealmAuthenticator类中doAuthenticate(AuthenticationToken authenticationToken)

    最终会到CustomRealm#doGetAuthenticationInfo方法进行登录认证

    /**
         * 登录认证
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException
        {
            UsernamePasswordToken upToken = (UsernamePasswordToken) token;
            String username = upToken.getUsername();
            String password = "";
            if (upToken.getPassword() != null)
            {
                password = new String(upToken.getPassword());
            }
    
            SysUser user = null;
            try
            {
                user = loginService.login(username, password);
            }
            catch (Exception e)
            {
                log.info("对用户[" + username + "]进行登录验证..验证未通过{}", e.getMessage());
                throw new AuthenticationException(e.getMessage(), e);
            }
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName());
            return info;
        }
    

      

    在此方法中处理登录名和密码是否正确

    4.登录成功用户在访问具体资源时

    假如接口设置了@RequiresPermissions("system:user:view")

    会先CustomRealm#doGetAuthorizationInfo方法进行权限认证

    @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0)
        {
            SysUser user = ShiroUtils.getSysUser();
            // 角色列表
            Set<String> roles = new HashSet<String>();
            // 功能列表
            Set<String> menus = new HashSet<String>();
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            // 管理员拥有所有权限
            if (user.isAdmin())
            {
                info.addRole("admin");
                info.addStringPermission("*:*:*");
            }
            else
            {
                roles = roleService.selectRoleKeys(user.getUserId());
                menus = menuService.selectPermsByUserId(user.getUserId());
                // 角色加入AuthorizationInfo认证对象
                info.setRoles(roles);
                // 权限加入AuthorizationInfo认证对象
                info.setStringPermissions(menus);
            }
            return info;
        }

    注意: 在权限认证时候如果注入CustomRealm没有设置缓存会重复调用

    建议设置缓存

    //将自己的验证方式加入容器
        @Bean
        public CustomRealm myShiroRealm(EhCacheManager cacheManager) {
            CustomRealm customRealm = new CustomRealm();
            //设置缓存
            customRealm.setCacheManager(cacheManager);
            return customRealm;
        }

    默认使用ehcache缓存

    redis使用方式参考: https://blog.csdn.net/qq_34021712/article/details/80791219

    自定义RedisCacheManager 实现 CacheManager

    注入到SecurityManager中

    //配置redis缓存

    securityManager.setCacheManager(cacheManager());

    //配置自定义session管理,使用redis

    securityManager.setSessionManager(sessionManager());

  • 相关阅读:
    Java类加载器回顾
    2018第24周总结
    JUC类图
    CopyOnWrite 策略
    Python导入模块的几种姿势
    查看linux接口进出口流量的命令;linux 网络监控;流量监控
    jenkins修改日志级别方法
    top命令查看线程信息和jstack使用介绍
    How to force immediate stop of threads in Jmeter servers如何在jmeter执行完,立即停止jmeter
    pycharm支持react
  • 原文地址:https://www.cnblogs.com/yxgmagic/p/15607787.html
Copyright © 2020-2023  润新知