• springboot+shiro+redis(单机redis版)整合教程-续(添加动态角色权限控制)


    相关教程:

     1. springboot+shiro整合教程

     2. springboot+shiro+redis(单机redis版)整合教程

     3. springboot+shiro+redis(集群redis版)整合教程

    参考此教程前请先阅读 2.springboot+shiro+redis(单机redis版)整合教程,此教程是在其基础上进行修改添加动态角色权限的。

    本教程整合环境: java8 maven redis(单机)

    开发工具: idea

    版本: springboot 1.5.15.RELEASE

    注:

    1.本教程数据操作是模拟数据库操作,并没有真正进行持久化,自行修改即可。

    项目结构,在 springboot+shiro+redis(单机redis版)整合教程 基础上进行的添加和修改结构如下:

    首先添加角色权限实体类RolesPermissions.java

    package webapp.model;
    
    import lombok.Data;
    
    import java.io.Serializable;
    
    @Data
    public class RolesPermissions implements Serializable {
        private static final long serialVersionUID = 1L;
    
        /**
         * @备注:
         * @字段:id BIGINT(19)
         */
        private Long id;
    
    
        /**
         * @备注:创建时间
         * @字段:create_time DATETIME(19)
         */
        private java.util.Date createTime;
    
    
        /**
         * @备注:更新时间
         * @字段:update_time DATETIME(19)
         */
        private java.util.Date updateTime;
    
        /**
         * @备注:角色id
         * @字段:roles_id BIGINT(19)
         */
        private Long rolesId;
    
    
        /**
         * @备注:权限id
         * @字段:permissions_id BIGINT(19)
         */
        private Long permissionsId;
    
    
        /**
         * @备注:角色名称
         * @字段:roles_name VARCHAR(100)
         */
        private String rolesName;
    
    
        /**
         * @备注:api接口
         * @字段:url VARCHAR(512)
         */
        private String url;
    
    
    }

    添加service层 RolesPermissionsService.java和 RolesPermissionsServiceImpl.java

    RolesPermissionsService.java:

    package webapp.service;
    
    import webapp.model.RolesPermissions;
    
    import java.util.List;
    
    public interface RolesPermissionsService {
        List<RolesPermissions> selectList();
        List<RolesPermissions> selectByAuserName(String auserName);
    }

    RolesPermissionsServiceImpl.java:

    package webapp.service.impl;
    
    import org.springframework.stereotype.Service;
    import webapp.model.RolesPermissions;
    import webapp.service.RolesPermissionsService;
    
    import java.util.LinkedList;
    import java.util.List;
    
    @Service
    public class RolesPermissionsServiceImpl implements RolesPermissionsService {
        @Override
        public List<RolesPermissions> selectList() {
            List<RolesPermissions> rolesPermissionsList = new LinkedList<>();
    
            RolesPermissions rolesPermissions = new RolesPermissions();
            rolesPermissions.setId(1L);
            rolesPermissions.setPermissionsId(1L);
            rolesPermissions.setRolesId(1L);
            rolesPermissions.setRolesName("超级管理员");
            rolesPermissions.setUrl("/index.html");
            rolesPermissionsList.add(rolesPermissions);
    
            RolesPermissions rolesPermissions2 = new RolesPermissions();
            rolesPermissions2.setId(2L);
            rolesPermissions2.setPermissionsId(1L);
            rolesPermissions2.setRolesId(2L);
            rolesPermissions2.setRolesName("管理员");
            rolesPermissions2.setUrl("/test.html");
            rolesPermissionsList.add(rolesPermissions2);
    
            RolesPermissions rolesPermissions3 = new RolesPermissions();
            rolesPermissions3.setId(3L);
            rolesPermissions3.setPermissionsId(1L);
            rolesPermissions3.setRolesId(2L);
            rolesPermissions3.setRolesName("超级管理员");
            rolesPermissions3.setUrl("/test.html");
            rolesPermissionsList.add(rolesPermissions3);
    
            RolesPermissions rolesPermissions4 = new RolesPermissions();
            rolesPermissions4.setId(4L);
            rolesPermissions4.setPermissionsId(3L);
            rolesPermissions4.setRolesId(1L);
            rolesPermissions4.setRolesName("超级管理员");
            rolesPermissions4.setUrl("/core/user/findUser");
            rolesPermissionsList.add(rolesPermissions4);
    
            return rolesPermissionsList;
        }
    
        @Override
        public List<RolesPermissions> selectByAuserName(String auserName) {
            List<RolesPermissions> rolesPermissionsList = new LinkedList<>();
            RolesPermissions rolesPermissions = new RolesPermissions();
            rolesPermissions.setId(1L);
            rolesPermissions.setPermissionsId(1L);
            rolesPermissions.setRolesId(1L);
            rolesPermissions.setRolesName("运营管理员");
            rolesPermissions.setUrl("/index.html");
            rolesPermissionsList.add(rolesPermissions);
            return rolesPermissionsList;
        }
    }

     修改登录相关接口 UserController.java:

    package webapp.controller;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.session.Session;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.apache.shiro.subject.Subject;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.data.redis.core.RedisTemplate;
    import org.springframework.web.bind.annotation.*;
    import webapp.service.UserService;
    
    import javax.annotation.Resource;
    import java.io.Serializable;
    
    /**
     * Created by Administrator on 2018/9/5.
     */
    @RestController
    @RequestMapping("/core/user")
    public class UserController {
        @Autowired
        private UserService userService;
    
        /**
         * 登录
         * @param
         * @return
         */
        @GetMapping("/login")
        public String login(String userName, String password) {
            System.out.println("登录" + userName);
    
            Subject subject = SecurityUtils.getSubject();
    
            UsernamePasswordToken token = new UsernamePasswordToken("admin-pc==" + userName, password);
            subject.login(token);
    
            Session session = subject.getSession();
            Serializable sessionId = session.getId();
            System.out.println("登录成功 -> " + sessionId);
    
            return userName + "[" + sessionId + "]";
        }
    
        @GetMapping("/logout")
        public String logout() {
            SecurityUtils.getSubject().logout();
            return "退出登录成功";
        }
    
        /**
         * 获取当前登录用户
         * @return
         */
        @GetMapping("/findUser")
        public String findUser() {
            Subject subject = SecurityUtils.getSubject();
            PrincipalCollection collection = subject.getPrincipals();
            if (null != collection && !collection.isEmpty()) {
                String userName = (String) collection.iterator().next();
                System.out.println("获取当前登录用户" + userName);
                return userService.findOneByUserName(userName).toString();
            }
            return "{
    " +
                    "    "codeEnum": "OVERTIME",
    " +
                    "    "code": 0,
    " +
                    "    "data": null,
    " +
                    "    "msg": "未登陆/登陆超时",
    " +
                    "    "success": false
    " +
                    "}";
        }
    }

    修改授权相关 UserShiroRealm.java:

    package webapp.shiro;
    
    import org.apache.shiro.SecurityUtils;
    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.session.Session;
    import org.apache.shiro.session.mgt.eis.SessionDAO;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.subject.support.DefaultSubjectContext;
    import org.springframework.beans.factory.annotation.Autowired;
    import webapp.model.RolesPermissions;
    import webapp.model.User;
    import webapp.service.RolesPermissionsService;
    import webapp.service.UserService;
    
    import java.util.Collection;
    import java.util.List;
    import java.util.stream.Collectors;
    
    /**
     * Created by Administrator on 2018/9/5.
     */
    public class UserShiroRealm extends AuthorizingRealm {
        @Autowired
        private UserService userService;
        @Autowired
        private SessionDAO sessionDAO;
        @Autowired
        private RolesPermissionsService rolesPermissionsService;
    
        /**
         * 角色权限和对应权限添加
         * @param principalCollection
         * @return
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            String userName = (String) principalCollection.getPrimaryPrincipal();
    
            if (userName == null || "".equals(userName)) {
                return null;
            }else{
                userName = userName.split("==")[1];
                SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
                try{
                    List<RolesPermissions> rolesPermissions = rolesPermissionsService.selectByAuserName(userName);
                    //当前用户具有的权限
                    List<String> roles = rolesPermissions.stream().map(RolesPermissions::getRolesName).collect(Collectors.toList());
                    authorizationInfo.addRoles(roles);
                }catch(Exception e){
                    e.printStackTrace();
                }
                return authorizationInfo;
            }
        }
    
        /**
         * 用户认证
         * @param authenticationToken
         * @return
         * @throws AuthenticationException
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            //加这一步的目的是在Post请求的时候会先进认证,然后在到请求
            if (authenticationToken.getPrincipal() == null) {
                return null;
            }
    
            String userName = authenticationToken.getPrincipal().toString();
    
            //只允许同一账户单个登录
            Subject subject = SecurityUtils.getSubject();
            Session nowSession = subject.getSession();
            Collection<Session> sessions = sessionDAO.getActiveSessions();
            if(sessions != null && sessions.size() > 0) {
                for (Session session : sessions) {
                    if (!nowSession.getId().equals(session.getId()) && (session.getTimeout() == 0
                            || userName.equals(String.valueOf(session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY))))) {
                        sessionDAO.delete(session);
                    }
                }
            }
    
            User user = userService.findOneByUserName(userName.contains("admin-pc==") ? userName.split("==")[1] : userName);
            if (user == null) {
                return null;
            } else {
                //这里验证authenticationToken和simpleAuthenticationInfo的信息
                return new SimpleAuthenticationInfo(userName, user.getPassword(), getName());
            }
        }
    }

    自定义角色权限校验器roleOrFilter(注:默认的roles为and关系,由于实际项目需要更多为或者关系,故自定义此类) CustomRolesAuthorizationFilter.java:

    package webapp.shiro;
    
    import org.apache.shiro.subject.PrincipalCollection;
    import org.apache.shiro.subject.Subject;
    import org.apache.shiro.web.filter.authz.AuthorizationFilter;
    import webapp.model.RolesPermissions;
    import webapp.redis.RedisCache;
    import webapp.service.RolesPermissionsService;
    
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.List;
    import java.util.Set;
    import java.util.stream.Collectors;
    
    public class CustomRolesAuthorizationFilter extends AuthorizationFilter {
        //项目名-用于多项目共用redis缓存时区分项目
        private static final String PROJECTNAME = "shiro5";
        private RolesPermissionsService rolesPermissionsService;
        private RedisCache redisCache;
    
        public CustomRolesAuthorizationFilter(RolesPermissionsService rolesPermissionsService, RedisCache redisCache) {
            this.rolesPermissionsService = rolesPermissionsService;
            this.redisCache = redisCache;
        }
    
        @Override
        public boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
            Subject subject = getSubject(request, response);
            PrincipalCollection principals = subject.getPrincipals();
    
            //未登录情况
            if (null == principals) {
                return false;
            }
    
            String userName = principals.toString();
            String userName0 = userName.split("==")[0];
    
            String[] rolesArray = (String[]) mappedValue;
            if (rolesArray == null || rolesArray.length == 0) {
                return true;
            }
    
            //当前请求URI所需要的角色
            List<String> rolesList = Arrays.asList(rolesArray);
    
            String userName1 = "";
            //redis缓存中当前登录用户的角色(注:更改用户角色时需要更新此缓存)
            Object cache = redisCache.getCache(PROJECTNAME + "==isAccessAllowed==" + userName);
    
            if ("admin-pc".equals(userName0)) {
                if (null != cache) {
                    Set<String> cache1 = (Set<String>) cache;
                    boolean disjoint = Collections.disjoint(cache1, rolesList);
                    return !disjoint;
                }
                userName1 = userName.split("==")[1];
            }
    
            List<RolesPermissions> rolesPermissions;
            if ("admin-pc".equals(userName0)) {
                rolesPermissions = rolesPermissionsService.selectByAuserName(userName1);
            } else {
                return true;
            }
    
            //当前用户具有的权限
            Set<String> roles = rolesPermissions.stream().map(RolesPermissions::getRolesName).collect(Collectors.toSet());
    
            //往redis缓存中存储当前登录用户的角色(注:更改用户角色时需要更新此缓存)
            redisCache.putCacheWithExpireTime(PROJECTNAME + "==isAccessAllowed==" + userName, roles, 86400); //24小时过期
    
            boolean disjoint = Collections.disjoint(roles, rolesList);
    
            return !disjoint;
    
        }
    
    }

    使用自定义的角色权限校验器roleOrFilter,ShiroPermissionFactory.java:

    package webapp.shiro;
    
    import org.apache.shiro.config.Ini;
    import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    import org.apache.shiro.web.config.IniFilterChainResolverFactory;
    import org.springframework.util.CollectionUtils;
    import webapp.model.RolesPermissions;
    import webapp.service.RolesPermissionsService;
    
    import java.util.*;
    
    public class ShiroPermissionFactory extends ShiroFilterFactoryBean {
        /**记录配置中的过滤链*/
        public static String definition = "";
        private RolesPermissionsService rolesPermissionsService;
    
        public ShiroPermissionFactory(RolesPermissionsService rolesPermissionsService){
            this.rolesPermissionsService = rolesPermissionsService;
        }
    
        /**
         * 初始化设置过滤链
         */
        @Override
        public void setFilterChainDefinitions(String definitions) {
    
            definition = definitions;//记录配置的静态过滤链
    
            List<RolesPermissions> rolesPermissions = rolesPermissionsService.selectList();
            Set<String> urls = new LinkedHashSet<>();
            for (RolesPermissions rolesPermission : rolesPermissions) {
                urls.add(rolesPermission.getUrl());
            }
    
            Map<String,String> otherChains = new HashMap<>();
            for (String url : urls) {
                StringBuilder roleOrFilters = new StringBuilder();
                int j = 0;
                for (int i = 0; i < rolesPermissions.size(); i++) {
                    if (Objects.equals(url, rolesPermissions.get(i).getUrl())) {
                        if (j == 0) {
                            j = 1;
                            roleOrFilters.append(rolesPermissions.get(i).getRolesName());
                        } else {
                            roleOrFilters.append(",").append(rolesPermissions.get(i).getRolesName());
                        }
                    }
                }
                String rolesStr = roleOrFilters.toString();
                if (!"".equals(rolesStr)) {
                    otherChains.put(url, "roleOrFilter[" + rolesStr + "]");
                }
            }
            //加载配置默认的过滤链
            Ini ini = new Ini();
            ini.load(definitions);
            Ini.Section section = ini.getSection(IniFilterChainResolverFactory.URLS);
            if (CollectionUtils.isEmpty(section)) {
                section = ini.getSection(Ini.DEFAULT_SECTION_NAME);
            }
            //加上数据库中过滤链
            section.putAll(otherChains);
            setFilterChainDefinitionMap(section);
        }
    }

    shiro配置类ShiroConfig.java修改如下:

    package webapp.conf;
    
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.spring.LifecycleBeanPostProcessor;
    import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.apache.shiro.web.servlet.SimpleCookie;
    import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import webapp.redis.RedisCache;
    import webapp.redis.RedisSessionDAO;
    import webapp.service.impl.RolesPermissionsServiceImpl;
    import webapp.shiro.CustomRolesAuthorizationFilter;
    import webapp.shiro.ShiroPermissionFactory;
    import webapp.shiro.UserShiroRealm;
    
    import javax.servlet.Filter;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * shiro配置类
     * Created by Administrator on 2018/9/5.
     */
    @Configuration
    public class ShiroConfig {
    
        //将自己的验证方式加入容器
        @Bean
        public UserShiroRealm userShiroRealm() {
            return new UserShiroRealm();
        }
    
        @Bean
        public SimpleCookie getSimpleCookie() {
            SimpleCookie simpleCookie = new SimpleCookie();
            simpleCookie.setName("SHRIOSESSIONID");
            return simpleCookie;
        }
    
        //保证实现了Shiro内部lifecycle函数的bean执行
        @Bean
        public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
            return new LifecycleBeanPostProcessor();
        }
    
        //配置shiro session 的一个管理器
        @Bean(name = "sessionManager")
        public DefaultWebSessionManager getDefaultWebSessionManager(RedisSessionDAO redisSessionDAO) {
            DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
            sessionManager.setSessionDAO(redisSessionDAO);
            sessionManager.setGlobalSessionTimeout(-1000);  //session有效期 默认值1800000 30分钟 1800000毫秒  -1000表示永久
            SimpleCookie simpleCookie = getSimpleCookie();
            simpleCookie.setHttpOnly(true);                 //设置js不可读取此Cookie
            simpleCookie.setMaxAge(3 * 365 * 24 * 60 * 60); //3年 cookie有前期
            sessionManager.setSessionIdCookie(simpleCookie);
            return sessionManager;
        }
    
        //配置核心安全事务管理器
        @Bean(name="securityManager")
        public SecurityManager securityManager(@Qualifier("userShiroRealm") UserShiroRealm userShiroRealm,
                                               @Qualifier("sessionManager") DefaultWebSessionManager sessionManager) {
            DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
            manager.setRealm(userShiroRealm);
            manager.setSessionManager(sessionManager);
            return manager;
        }
    
        //权限管理,配置主要是Realm的管理认证
        @Bean
        public SecurityManager securityManager() {
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            securityManager.setRealm(userShiroRealm());
            return securityManager;
        }
    
        //Filter工厂,设置对应的过滤条件和跳转条件
        @Bean
        public ShiroPermissionFactory shiroPermissionFactory(RedisCache redisCache) {
            ShiroPermissionFactory shiroFilter = new ShiroPermissionFactory(new RolesPermissionsServiceImpl());
            shiroFilter.setSecurityManager(securityManager());
    
            //登录认证不通过跳转
            shiroFilter.setLoginUrl("/loginUnAuth");
            //权限认证不通过跳转
            shiroFilter.setUnauthorizedUrl("/authorUnAuth");
    
            Map<String, Filter> filters = new HashMap<>();
            filters.put("roleOrFilter", new CustomRolesAuthorizationFilter(new RolesPermissionsServiceImpl(), redisCache));
            shiroFilter.setFilters(filters);
    
            shiroFilter.setFilterChainDefinitions("/###/@@@/** = authc");
            return shiroFilter;
        }
    
        //加入注解的使用,不加入这个注解不生效
        @Bean
        public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
            AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
            authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
            return authorizationAttributeSourceAdvisor;
        }
    }

    角色权限刷新器FilterChainDefinitions.java:

    package webapp.shiro;
    
    import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
    import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
    import org.apache.shiro.web.servlet.AbstractShiroFilter;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import org.springframework.util.CollectionUtils;
    
    import java.util.Map;
    
    /**
     * 当修改用户角色权限时候需要调用reloadFilterChains()来刷新角色权限
     */
    @Component
    public class FilterChainDefinitions {
        @Autowired
        private ShiroPermissionFactory permissFactory;
    
        public void reloadFilterChains() {
            synchronized (permissFactory) {   //强制同步,控制线程安全
                AbstractShiroFilter shiroFilter = null;
    
                try {
                    shiroFilter = (AbstractShiroFilter) permissFactory.getObject();
    
                    PathMatchingFilterChainResolver resolver = (PathMatchingFilterChainResolver) shiroFilter
                            .getFilterChainResolver();
                    // 过滤管理器
                    DefaultFilterChainManager manager = (DefaultFilterChainManager) resolver.getFilterChainManager();
                    // 清除权限配置
                    manager.getFilterChains().clear();
                    permissFactory.getFilterChainDefinitionMap().clear();
                    // 重新设置权限
                    permissFactory.setFilterChainDefinitions(ShiroPermissionFactory.definition);//传入配置中的filterchains
    
                    Map<String, String> chains = permissFactory.getFilterChainDefinitionMap();
                    //重新生成过滤链
                    if (!CollectionUtils.isEmpty(chains)) {
                        chains.forEach((url, definitionChains) -> {
                            manager.createChain(url, definitionChains.trim().replace(" ", ""));
                        });
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    角色权限刷新器FilterChainDefinitions.java的使用示例TestController.java:

    package webapp.controller;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    import webapp.shiro.FilterChainDefinitions;
    
    @RestController
    @RequestMapping("/core/test")
    public class TestController {
        @Autowired
        private FilterChainDefinitions filterChainDefinitions;
    
    
        @GetMapping("/test")
        public void test() {
            //设置用户权限
            //...
            //刷新权限
            filterChainDefinitions.reloadFilterChains();
        }
    }

    然后启动项目,RolesPermissionsServiceImpl.java类中的获取数据库(模拟)中所有角色权限和获取用户角色权限可自行修改测试。实际应用于项目也只需要修改这个类为查询实际数据库数据即可。

  • 相关阅读:
    IIS配置桌面上的文件报错,可能是含有中文字符
    GridView序号
    图片处理函数(自适应缩略图datatable中添加缩略图像)
    linqtoobject
    数据库存取图片二进制数据
    反射
    jquery
    加干扰字符
    Doc的窗口就创建一个,如果已经存在就激活那个窗口,不存在就建立Doc(转自CSDN)
    VC++中使用内存映射文件处理大文件(转)
  • 原文地址:https://www.cnblogs.com/007sx/p/9640685.html
Copyright © 2020-2023  润新知