springboot+shiro+redis实现同时对角色和用户的权限控制

前言:首先说明,这个需求很少(奇)见(葩)(由一个不懂代码的产品经理提出),要求公司内部后台系统里面,既能按照部门职能控制访问权限,又要能随意给某个人赋予权限。

二话不说,上配置。

1.数据库配置

数据库表为6个分别是: 用户表、角色表、权限表、用户角色中间表、角色权限中间表,用户权限中间表(实现对单独用户控制权限的关键)![](https://img-blog.csdnimg.cn/20191025165000819.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NDgyODgwOA==,size_16,color_FFFFFF,t_70)

2.引入依赖

 <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
        <mybatis.version>3.4.5</mybatis.version>
        <commons-lang3.version>3.6</commons-lang3.version>
        <mybatis-spring-boot.version>1.3.1</mybatis-spring-boot.version>
        <fastjson.version>1.2.30</fastjson.version>
        <druid.version>1.1.6</druid.version>
        <shiro.version>1.4.0</shiro.version>
        <lombok.version>1.16.18</lombok.version>
    </properties>


    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>

        <!--<dependency>-->
        <!--<groupId>org.springframework.boot</groupId>-->
        <!--<artifactId>spring-boot-starter-security</artifactId>-->
        <!--</dependency>-->

        <!--<dependency>-->
        <!--<groupId>org.springframework.security</groupId>-->
        <!--<artifactId>spring-security-test</artifactId>-->
        <!--<scope>test</scope>-->
        <!--</dependency>-->

        <!--消息中间件-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>
       <!-- <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-websocket</artifactId>
        </dependency>-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>${mybatis-spring-boot.version}</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>${druid.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>com.vaadin.external.google</groupId>
                    <artifactId>android-json</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>
        <!-- cxf支持 -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5</version>
            <exclusions>
                <exclusion>
                    <artifactId>commons-codec</artifactId>
                    <groupId>commons-codec</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>${commons-lang3.version}</version>
        </dependency>

        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.2.4</version>
            <exclusions>
                <exclusion>
                    <artifactId>mybatis-spring-boot-starter</artifactId>
                    <groupId>org.mybatis.spring.boot</groupId>
                </exclusion>
            </exclusions>
        </dependency>

        <!--阿里云OSS-->
        <dependency>
            <groupId>com.aliyun.oss</groupId>
            <artifactId>aliyun-sdk-oss</artifactId>
            <version>2.8.3</version>
            <exclusions>
                <exclusion>
                    <artifactId>httpclient</artifactId>
                    <groupId>org.apache.httpcomponents</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-dysmsapi</artifactId>
            <version>1.1.0</version>
        </dependency>
        <!--阿里云核心-->
        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-core</artifactId>
            <version>4.4.3</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>

        <!--极光推送-->
        <dependency>
            <groupId>cn.jpush.api</groupId>
            <artifactId>jiguang-common</artifactId>
            <version>1.1.4</version>
        </dependency>
        <dependency>
            <groupId>cn.jpush.api</groupId>
            <artifactId>jpush-client</artifactId>
            <version>3.3.10</version>
            <exclusions>
                <exclusion>
                    <artifactId>jiguang-common</artifactId>
                    <groupId>cn.jpush.api</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>httpclient</artifactId>
                    <groupId>org.apache.httpcomponents</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--压缩-->
        <dependency>
            <groupId>net.coobird</groupId>
            <artifactId>thumbnailator</artifactId>
            <version>0.4.8</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.5</version>
        </dependency>

        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.8</version>
        </dependency>
        <dependency>
            <groupId>com.google.zxing</groupId>
            <artifactId>javase</artifactId>
            <version>3.0.0</version>
        </dependency>

        <!-- HSSF需要引入的 -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>RELEASE</version>
            <exclusions>
                <exclusion>
                    <artifactId>commons-codec</artifactId>
                    <groupId>commons-codec</groupId>
                </exclusion>
            </exclusions>
        </dependency>

        <!-- XSSF需要引入的 -->
        <dependency>
            <groupId>org.apache.xmlbeans</groupId>
            <artifactId>xmlbeans</artifactId>
            <version>2.6.0</version>
        </dependency>
        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version><!--$NO-MVN-MAN-VER$ -->
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>3.9</version>
            <exclusions>
                <exclusion>
                    <artifactId>commons-codec</artifactId>
                    <groupId>commons-codec</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.9</version>
            <exclusions>
                <exclusion>
                    <artifactId>xmlbeans</artifactId>
                    <groupId>org.apache.xmlbeans</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--8.添加json数据处理的依赖  jackson-databind  -->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.8</version>
        </dependency>
        <!--shiro的依赖-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-all</artifactId>
            <version>1.3.2</version>
            <exclusions>
                <exclusion>
                    <artifactId>shiro-core</artifactId>
                    <groupId>org.apache.shiro</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>${shiro.version}</version>
        </dependency>
        <!-- shiro+redis缓存插件 -->
        <dependency>
            <groupId>org.crazycake</groupId>
            <artifactId>shiro-redis</artifactId>
            <version>3.1.0</version>
            <exclusions>
                <exclusion>
                    <artifactId>shiro-core</artifactId>
                    <groupId>org.apache.shiro</groupId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>com.vdurmont</groupId>
            <artifactId>emoji-java</artifactId>
            <version>4.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
        </dependency>

        <dependency>
            <groupId>org.yeauty</groupId>
            <artifactId>netty-websocket-spring-boot-starter</artifactId>
            <version>0.8.0</version>
        </dependency>
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk15on</artifactId>
            <version>1.56</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-solr</artifactId>
        </dependency>
    </dependencies>

3.yml文件的配置(其他配置不再赘述)

    # 配置redis
  redis:
    host: 172.18.1.166
    port: 6379
    timeout: 6000ms
    

4.配置shirofilter

package com.longjie.yunxin.common.configuration;


import com.longjie.yunxin.common.util.MyCredentialsMatcher;
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.apache.shiro.web.servlet.SimpleCookie;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.CrossOrigin;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;


@Configuration
@CrossOrigin
public class ShiroConfiguration {

    /**
     * 注入配置文件属性
     */
    @Value("${spring.redis.host}")
    private String host;//地址
    @Value("${spring.redis.port}")
    private int port;
    @Value("${spring.redis.timeout}")
    private String timeout;//过期时间
    //将自己的验证方式加入容器
    @Bean
    public ShiroRealm ShiroRealm() {
        ShiroRealm myShiroRealm = new ShiroRealm();
        myShiroRealm.setCredentialsMatcher(credentialsMatcher());
        return myShiroRealm;
    }
    @Bean
    public MyCredentialsMatcher credentialsMatcher() {
        return new MyCredentialsMatcher();
    }
    //Filter工厂,设置对应的过滤条件和跳转条件
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 自定义拦截器的配置
        Map<String, Filter> filter = new HashMap<>();
        filter.put("authc", new ShiroUserFilter());
        shiroFilterFactoryBean.setFilters(filter);
        // 没有登陆的用户只能访问登陆页面,前后端分离中登录界面跳转应由前端路由控制,后台仅返回json数据
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        // 静态资源
        filterChainDefinitionMap.put("/shiro/**", "authc");
        filterChainDefinitionMap.put("/Iuser/**", "authc");
        filterChainDefinitionMap.put("/Imerchant/**", "authc");
        filterChainDefinitionMap.put("/poi/**", "anon");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;

    }
    

    //权限管理,配置主要是Realm的管理认证
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(ShiroRealm());
        // 自定义缓存实现 使用redis
        securityManager.setCacheManager(cacheManager());
        // 自定义session管理 使用redis
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }

    /**
     * cacheManager 缓存 redis实现
     * 使用的是shiro-redis开源插件
     *
     * @return
     */
    public RedisCacheManager cacheManager() {
        RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        redisCacheManager.setKeyPrefix("SPRINGBOOT_CACHE:");   //设置前缀
        return redisCacheManager;
    }

    /**
     * Session Manager
     * 使用的是shiro-redis开源插件
     */
    @Bean
    public SessionManager sessionManager() {
        SimpleCookie simpleCookie = new SimpleCookie("token");
        simpleCookie.setPath("/");
        simpleCookie.setHttpOnly(false);

        SessionManager sessionManager = new SessionManager();
        sessionManager.setSessionDAO(redisSessionDAO());
        sessionManager.setSessionIdCookieEnabled(false);
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        sessionManager.setDeleteInvalidSessions(true);
        sessionManager.setSessionIdCookie(simpleCookie);
        sessionManager.setGlobalSessionTimeout(1800 * 1000);//单位毫秒
        return sessionManager;
    }

    /**
     * 配置shiro redisManager
     * 使用的是shiro-redis开源插件
     *
     * @return
     */
    public RedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(host);
        redisManager.setPort(port);
        redisManager.setTimeout(Integer.parseInt(timeout.replace("ms","")));
        // redisManager.setPassword(password);
        return redisManager;
    }


    /**
     * RedisSessionDAO shiro sessionDao层的实现 通过redis
     * 使用的是shiro-redis开源插件
     */
    @Bean
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        redisSessionDAO.setKeyPrefix("SPRINGBOOT_SESSION:");
        redisSessionDAO.setExpire(1800);//设置过期时间,单位s
        return redisSessionDAO;
    }

    /***
     * 授权所用配置
     *
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }
    //加入注解的使用,不加入这个注解不生效
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
    /**
     * Shiro生命周期处理器
     * 此方法需要用static作为修饰词,否则无法通过@Value()注解的方式获取配置文件的值
     *
     */
    @Bean
    public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }


}

5.配置shiroRealm

package com.longjie.yunxin.common.configuration;


import com.longjie.yunxin.pojo.Manager;
import com.longjie.yunxin.pojo.Resource;
import com.longjie.yunxin.service.Shiro.ShiroService;
import org.apache.shiro.SecurityUtils;
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.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Collection;
import java.util.List;



public class ShiroRealm extends AuthorizingRealm {
    @Autowired
    private ShiroService shiroService;


    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
        UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
        // 从数据库获取对应用户名密码的用户
        Manager manager = shiroService.selectManagerByPhone(token.getUsername());
        getAuthorizationCache().clear();//清除当前用户的权限信息缓存
        if (manager != null) {
            //单用户登录
            //处理session
            DefaultWebSecurityManager securityManager = (DefaultWebSecurityManager) SecurityUtils.getSecurityManager();
            DefaultWebSessionManager sessionManager = (DefaultWebSessionManager) securityManager.getSessionManager();
            //获取当前已登录的用户session列表
            Collection<Session> sessions = sessionManager.getSessionDAO().getActiveSessions();
            Manager temp;
            for(Session session : sessions) {
                //清除该用户以前登录时保存的session,强制退出
                Object attribute = session.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
                if (attribute == null) {
                    continue;
                }

                temp = (Manager) ((SimplePrincipalCollection) attribute).getPrimaryPrincipal();
                if (token.getUsername().equals(temp.getUsername())) {
                    sessionManager.getSessionDAO().delete(session);
                }
            }
            SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                    manager, //用户
                    manager.getUserpwd(), //密码
                    getName()  //realm name
            );
            return authenticationInfo;
        }
        throw new UnknownAccountException();
    }
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0) {
        // TODO Auto-generated method stub
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        //获取登录时查询到的用户对象
        Manager manager = (Manager)arg0.getPrimaryPrincipal();
        //获取登录时查询到的用户对象
        List<Resource> permissions = shiroService.getPermissionByid(manager.getId());
        //把用户的所有权限添加到info中
        for(Resource resource : permissions){
            info.addStringPermission(resource.getUrl());
        }
        return info;
    }

    public void clearAuthz(){
        this.clearCachedAuthorizationInfo(SecurityUtils.getSubject().getPrincipals());
    }
}


6.配置ShiroUserFilter(解决我们项目前后端分离中出现跨域问题)

package com.longjie.yunxin.common.configuration;

/**
 * @description: shiro跨域解决
 * @author: 马志磊
 * @create: 2019-06-26 17:45
 */

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.UserFilter;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 重写shiro的UserFilter,实现通过OPTIONS请求
 *
 * @author MDY
 */
public class ShiroUserFilter extends UserFilter {
    /**
     * 在访问过来的时候检测是否为OPTIONS请求,如果是就直接返回true
     */
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
        Subject subject = SecurityUtils.getSubject();
        Session s = subject.getSession();
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        if(s==null){
            //跳转到登陆界面
            httpResponse.setHeader("result", "2");
            return false;
        }

        HttpServletRequest httpRequest = (HttpServletRequest) request;
        if (httpRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
            setHeader(httpRequest, httpResponse);
            return true;
        }
        return super.preHandle(request, response);
    }

    /**
     * 该方法会在验证失败后调用,这里由于是前后端分离,后台不控制页面跳转
     * 因此重写改成传输JSON数据
     */
    @Override
    protected void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
        saveRequest(request);
        setHeader((HttpServletRequest) request, (HttpServletResponse) response);
    }

    /**
     * 为response设置header,实现跨域
     */
    private void setHeader(HttpServletRequest request, HttpServletResponse response) {
        //跨域的header设置
        response.setHeader("Access-control-Allow-Origin", request.getHeader("result"));
        response.setHeader("Access-Control-Allow-Methods", request.getMethod());
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Headers", request.getHeader("Access-Control-Request-Headers"));
        //防止乱码,适用于传输JSON数据
        response.setHeader("Content-Type", "application/json;charset=UTF-8");
        response.setStatus(HttpStatus.OK.value());
    }
}

7.整合Redis

package com.longjie.yunxin.common.configuration;

import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.SessionKey;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.session.mgt.WebSessionKey;
import org.apache.shiro.web.util.WebUtils;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;

public class SessionManager  extends DefaultWebSessionManager{
    private static final String AUTHORIZATION = "token";

    private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";

    public SessionManager() {
    }

    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        //获取请求头,或者请求参数中的token
        String id = StringUtils.isEmpty(WebUtils.toHttp(request).getHeader(AUTHORIZATION))
                ? request.getParameter(AUTHORIZATION) : WebUtils.toHttp(request).getHeader(AUTHORIZATION);
        // 如果请求头中有 Token 则其值为sessionId
        if (StringUtils.isNotEmpty(id)) {
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);

            return id;
        } else {
            // 否则按默认规则从cookie取sessionId
            return super.getSessionId(request, response);
        }
    }

    /**
     * 获取session 优化单次请求需要多次访问redis的问题
     *
     * @param sessionKey
     * @return
     * @throws UnknownSessionException
     */
    @Override
    protected Session retrieveSession(SessionKey sessionKey) throws UnknownSessionException {
        Serializable sessionId = getSessionId(sessionKey);

        ServletRequest request = null;
        if (sessionKey instanceof WebSessionKey) {
            request = ((WebSessionKey) sessionKey).getServletRequest();
        }

        if (request != null && null != sessionId) {
            Object sessionObj = request.getAttribute(sessionId.toString());
            if (sessionObj != null) {
                return (Session) sessionObj;
            }
        }

        Session session = super.retrieveSession(sessionKey);
        if (request != null && null != sessionId) {
            request.setAttribute(sessionId.toString(), session);
        }
        return session;
    }


}

8.无权限访问时,后端捕获异常并将状态返给前端

package com.longjie.yunxin.common.configuration;

import com.longjie.yunxin.entity.CommonCode;
import com.longjie.yunxin.entity.QueryResult;
import com.longjie.yunxin.entity.Result;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletResponse;

@ControllerAdvice
@ResponseBody
public class ExceptionAdvice {
    /**
     * shiro权限异常处理
     * @return
     */
    @ExceptionHandler(UnauthorizedException.class)
    public Result unauthorizedException(HttpServletResponse response){
        response.setHeader("result","4");//
        QueryResult queryResult = new QueryResult();
        return new Result(CommonCode.UNAUTHORISE, queryResult);
    }

}

9.登陆接口

	@CrossOrigin
@RestController
@RequestMapping("/login")
public class LoginController {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedisTemplate redisTemplate;
 /**
     * 后台系统登陆【马志磊修改6.15】
     *
     * @param userPhone
     * @param userPwd
     * @return
     * @throws Exception
     */
    @RequestMapping("/loginPCByPwd")
    public Result loginPCByPwd(String userPhone, String userPwd) {
        QueryResult queryResult = new QueryResult();
        if (StringUtils.isEmpty(userPhone)) {
            //用户为空
            ExceptionCast.cast(CommonCode.PHONE_PARAM, "login", "loginPCByPwd");
        }
        if (StringUtils.isEmpty(userPwd)) {
            //密码为空
            ExceptionCast.cast(CommonCode.CODE_PARAM, "login", "loginPCByPwd");
        }
        //判断手机号和密码是否符合要求
        if (!PhoneFormatCheckUtils.isChinaPhoneLegal(userPhone)) {
            ExceptionCast.cast(CommonCode.PHONE_PARAM, "login", "loginPCByPwd");
        }
        //String newuserPwd = Md5Util.encodeByMd5(userPwd);
        // 认证 Subject:主体
        Subject subject = SecurityUtils.getSubject();
        // 根据用户信息,组成用户令牌token
        UsernamePasswordToken Token = new UsernamePasswordToken(userPhone, userPwd, true);
        try {
            subject.login(Token);
        } catch (AuthenticationException e) {
            return new Result(CommonCode.NULL_USER, queryResult);
        }
        Manager manager = (Manager) subject.getPrincipal();
        JSON json = new JSONObject();
        ((JSONObject) json).put("token", subject.getSession().getId());
        String token = subject.getSession().getId().toString();
        //清除历史token
        String userToken = stringRedisTemplate.opsForValue().get(userPhone + "web");
        if (!StringUtils.isEmpty(userToken)) {
            stringRedisTemplate.delete(userToken);
            stringRedisTemplate.delete(userPhone + "web");
        }
        stringRedisTemplate.boundValueOps(userPhone + "web").set(token);
        stringRedisTemplate.boundValueOps(token).set(JSON.toJSONString(manager));
        queryResult.setToken(token);
        queryResult.setData(manager);
        return new Result(CommonCode.SUCCESS, queryResult);
    }

}

10.同一个帐号最大会话数也可以有shiro完成,但是我接手本项目的时候已经实现了,不再赘述。

二.接下来是代码层的,除了单独对用户控制权限,其他的地方没什么绕的

1.Controller

package com.longjie.yunxin.controller.Shiro;

import com.longjie.yunxin.common.annotation.LogAnno;
import com.longjie.yunxin.common.exception.ExceptionCast;
import com.longjie.yunxin.entity.CommonCode;
import com.longjie.yunxin.entity.QueryResult;
import com.longjie.yunxin.entity.Result;
import com.longjie.yunxin.pojo.Permission;
import com.longjie.yunxin.pojo.Post;
import com.longjie.yunxin.pojo.Resource;
import com.longjie.yunxin.pojo.UserRole;
import com.longjie.yunxin.service.Shiro.ShiroService;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * @Author 马志磊
 * @Description: 权限部分的所有接口 共计18个
 * @Return
 * @Date 2019/6/24 14:01
 */
@CrossOrigin
@RestController
@RequestMapping("/shiro")
public class ShiroController {
    @Autowired
    private ShiroService shiroService;

    //查询所有人的权限信息(对个人的权限配置考虑在权限表加上userid,关联起来,userid!=0时,再查询另一个sql)
    @RequestMapping("/getPermission")
    public Result getPsermission() {
        QueryResult queryResult = new QueryResult();
        List<Permission> permissions = shiroService.getPermission();
        queryResult.setData(permissions);
        return new Result(CommonCode.SUCCESS, queryResult);
    }

    //获取权限列表
    @RequestMapping("/permissionList")
    public Result PermissionList() {
        QueryResult queryResult = new QueryResult();
        List<Permission> permissions = shiroService.permissionList();
        queryResult.setData(permissions);
        return new Result(CommonCode.SUCCESS, queryResult);
    }

    //增加某个人的权限信息
    @LogAnno(operateType = "增加某个人的权限信息")
    @RequiresPermissions("/shiro/*")
    @RequestMapping("/upPermById")
    public Result upPermById(Integer userid, @RequestBody int permid[]) {
        QueryResult queryResult = new QueryResult();
        if (permid == null || permid.length == 0) {
            ExceptionCast.cast(CommonCode.DATA_FAIL, "ShiroController", "upPermById");
        }
        shiroService.upPermById(userid, permid);
        return new Result(CommonCode.SUCCESS, queryResult);
    }

    //获取所有的角色信息
    @RequiresPermissions("/shiro/*")
    @RequestMapping("/getRole")
    public Result getRole() {
        QueryResult queryResult = new QueryResult();
        queryResult.setData(shiroService.getRole());
        return new Result(CommonCode.SUCCESS, queryResult);
    }

    //增加分组
    @LogAnno(operateType = "增加分组")
    @RequiresPermissions("/shiro/*")
    @RequestMapping("/addRole")
    public Result addRole(String name, @RequestBody int array[]) {
        if (StringUtils.isEmpty(name)) {
            ExceptionCast.cast(CommonCode.DATA_FAIL, "ShiroController", "addRole");
        }
        if (array == null || array.length == 0) {
            ExceptionCast.cast(CommonCode.DATA_FAIL, "ShiroController", "addRole");
        }
        QueryResult queryResult = new QueryResult();
        shiroService.addRole(name, array);
        return new Result(CommonCode.SUCCESS, queryResult);
    }

    //修改分组权限
    @LogAnno(operateType = "修改分组权限")
    @RequiresPermissions("/shiro/*")
    @RequestMapping("/updateRole")
    public Result updateRole(String name, Integer id, @RequestBody int array[]) {
        if (id == null || "".equals(id)) {
            ExceptionCast.cast(CommonCode.DATA_FAIL, "ShiroController", "updateRole");
        }
        if (StringUtils.isEmpty(name)) {
            ExceptionCast.cast(CommonCode.DATA_FAIL, "ShiroController", "updateRole");
        }
        if (array == null || array.length == 0) {
            ExceptionCast.cast(CommonCode.DATA_FAIL, "ShiroController", "updateRole");
        }
        QueryResult queryResult = new QueryResult();
        shiroService.updateRole(name, id, array);
        return new Result(CommonCode.SUCCESS, queryResult);
    }

    //查看分组对应的权限
    @RequestMapping("/getPerById")
    public Result getPerById(Integer roleid) {
        if (roleid == null || "".equals(roleid)) {
            ExceptionCast.cast(CommonCode.DATA_FAIL, "ShiroController", "getPerById");
        }
        QueryResult queryResult = new QueryResult();
        queryResult.setData(shiroService.getRoleByID(roleid));
        return new Result(CommonCode.SUCCESS, queryResult);
    }

    //删除分组
    @LogAnno(operateType = "删除分组")
    @RequiresPermissions("/shiro/*")
    @RequestMapping("/deleteRole")
    public Result deleteRole(Integer roleid) {
        if (roleid == null || "".equals(roleid)) {
            ExceptionCast.cast(CommonCode.DATA_FAIL, "ShiroController", "deleteRole");
        }
        QueryResult queryResult = new QueryResult();
        shiroService.deleteRoleByID(roleid);
        return new Result(CommonCode.SUCCESS, queryResult);
    }

    //组内成员信息列表
    @RequiresPermissions("/shiro/*")
    @RequestMapping("/queryInRole")
    public Result queryInRole(Integer roleid) {
        QueryResult queryResult = new QueryResult();
        List<UserRole> list = shiroService.queryInRole(roleid);
        queryResult.setData(list);
        return new Result(CommonCode.SUCCESS, queryResult);
    }

    //查看组内某个成员的权限列表(Resource)
    @RequiresPermissions("/shiro/*")
    @RequestMapping("/getPermByID")
    public Result getPermByID(Integer id) {
        if (id == null) {
            ExceptionCast.cast(CommonCode.DATA_FAIL, "ShiroController", "getPermByID");
        }
        QueryResult queryResult = new QueryResult();
        List<Resource> list = shiroService.getPermissionByid(id);
        queryResult.setData(list);
        return new Result(CommonCode.SUCCESS, queryResult);
    }

    //向添加成员:姓名,手机号,密码(初始密码同手机号),roleid(向成员表和成员角色表同时插入数据),职位id
    @LogAnno(operateType = "添加用户")
    @RequiresPermissions("/shiro/*")
    @RequestMapping("/addManager")
    public Result addManager(String userName, String userPhone, Integer roleid, Integer postid) {
        if (StringUtils.isEmpty(userName) || StringUtils.isEmpty(userPhone) ||
                roleid == null || "".equals(roleid) || postid == null || "".equals(postid)) {
            ExceptionCast.cast(CommonCode.DATA_FAIL, "ShiroController", "addManager");
        }
        QueryResult queryResult = new QueryResult();
        shiroService.addManager(userName, userPhone, roleid, postid);
        return new Result(CommonCode.SUCCESS, queryResult);

    }

    /**
     * @Author 马志磊
     * @Description: 后台用户密码修改
     * @Return
     * @Date 2019/6/26 14:44
     */
    @LogAnno(operateType = "后台用户密码修改")
    @RequiresPermissions("/shiro/*")
    @RequestMapping("/updatePwdByPhone")
    public Result updatePwdByPhone(String userPhone, String oldpwd, String newPwd) {
        if (StringUtils.isEmpty(userPhone) || StringUtils.isEmpty(oldpwd) || StringUtils.isEmpty(newPwd)) {
            ExceptionCast.cast(CommonCode.DATA_FAIL, "ShiroController", "updatePwdByPhone");
        }
        QueryResult queryResult = new QueryResult();
        try {
            shiroService.updatePwdByPhone(userPhone, oldpwd, newPwd);
            return new Result(CommonCode.SUCCESS, queryResult);
        } catch (Exception e) {
            return new Result(CommonCode.CODE_PARAM, queryResult);
        }
    }

    //获取职位列表
    @RequiresPermissions("/shiro/*")
    @RequestMapping("/postList")
    public Result postList() {
        QueryResult queryResult = new QueryResult();
        List<Post> posts = shiroService.postList();
        queryResult.setData(posts);
        return new Result(CommonCode.SUCCESS, queryResult);
    }

    //增加职位
    @LogAnno(operateType = "增加职位")
    @RequiresPermissions("/shiro/*")
    @RequestMapping("/addPost")
    public Result addPost(String name) {
        if (StringUtils.isEmpty(name)) {
            ExceptionCast.cast(CommonCode.DATA_FAIL, "ShiroController", "addPost");
        }
        QueryResult queryResult = new QueryResult();
        queryResult.setData(shiroService.addPost(name));
        return new Result(CommonCode.SUCCESS, queryResult);
    }

    //删除组成员
    @LogAnno(operateType = "后台删除组成员")
    @RequiresPermissions("/shiro/*")
    @RequestMapping("/deleteManager")
    public Result deleteManager(Integer id) {
        if (id == null || "".equals(id)) {
            ExceptionCast.cast(CommonCode.DATA_FAIL, "ShiroController", "deleteManager");
        }
        QueryResult queryResult = new QueryResult();
        shiroService.deleteManager(id);
        return new Result(CommonCode.SUCCESS, queryResult);
    }

    /**
     * @Author 马志磊
     * @Description: 修改职位
     * @Return
     * @Date 2019/7/6 9:09
     */
    @LogAnno(operateType = "修改职位")
    @RequiresPermissions("/shiro/*")
    @RequestMapping("/updatePost")
    public Result updatePost(@RequestBody Post post) {
        if (post == null) {
            ExceptionCast.cast(CommonCode.DATA_FAIL, "ShiroController", "updatePost");
        }
        QueryResult queryResult = new QueryResult();
        shiroService.updatePost(post);
        return new Result(CommonCode.SUCCESS, queryResult);
    }

    /**
     * @Author 马志磊
     * @Description: 删除职位
     * @Return
     * @Date 2019/7/6 11:09
     */
    @LogAnno(operateType = "删除职位")
    @RequiresPermissions("/shiro/*")
    @RequestMapping("/deletePost")
    public Result deletePost(Integer id) {
        if (id == null || "".equals(id)) {
            ExceptionCast.cast(CommonCode.DATA_FAIL, "ShiroController", "deletePost");
        }
        QueryResult queryResult = new QueryResult();
        shiroService.deletePost(id);
        return new Result(CommonCode.SUCCESS, queryResult);
    }
}

(通过去重实现此功能,所以只能较组内成员增加权限,不能减少)

2.Service

package com.longjie.yunxin.service.Shiro;

import com.longjie.yunxin.pojo.*;

import java.util.List;

public interface ShiroService {
    //根据手机号查询用户
    Manager selectManagerByPhone(String phone);

    //获取所有的权限信息
    List<Permission> getPermission();

    //获取所有的权限信息
    List<Permission> permissionList();

    //修改某人的权限信息
    void upPermById(Integer userid, int permid[]);

    //根据id获取个人的权限信息
    List<Resource> getPermissionByid(Integer id);

    //获取所有的权限信息
    List<Role> getRole();

    //增加分组信息并关联权限
    void addRole(String name, int array[]);

    //修改分组信息和权限
    void updateRole(String name, Integer id, int array[]);

    //查询角色的权限
    Role getRoleByID(Integer roleid);

    //删除角色
    void deleteRoleByID(Integer roleid);

    //查询组内的信息
    List<UserRole> queryInRole(Integer roleid);

    //添加成员
    void addManager(String userName, String userPhone, Integer roleid, Integer postid);

    //修改密码
    void updatePwdByPhone(String userPhone, String oldpwd, String newPwd) throws Exception;

    //职位列表
    List<Post> postList();

    //增加职位
    int addPost(String name);

    //删除组成员
    void deleteManager(Integer id);

    //修改职位
    void updatePost(Post post);

    //删除职位
    int deletePost(Integer id);

}

3.ServicreImpl

package com.longjie.yunxin.service.Shiro.impl;

import com.longjie.yunxin.common.exception.ExceptionCast;
import com.longjie.yunxin.common.util.Md5Util;
import com.longjie.yunxin.controller.Shiro.CommonController;
import com.longjie.yunxin.entity.CommonCode;
import com.longjie.yunxin.mapper.ShiroMapper;
import com.longjie.yunxin.pojo.*;
import com.longjie.yunxin.service.Shiro.ShiroService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class ShiroServiceImpl implements ShiroService {
    @Autowired
    private ShiroMapper shiroMapper;

    @Override
    public Manager selectManagerByPhone(String phone) {
        return shiroMapper.selectManagerByPhone(phone);
    }

    @Override
    public List<Permission> getPermission() {
        return shiroMapper.selectPerm();
    }

    @Override
    public List<Permission> permissionList() {
        return shiroMapper.permissionList();
    }

    @Transactional
    @Override
    public void upPermById(Integer userid, int permid[]) {
        //先清除所有权限
        int updateNum = permid.length;
        List<Integer> ids = shiroMapper.findIdByManageerID(userid);
        int num = ids.size();
        if (updateNum > num) {
            for (int i = 0; i < num; i++) {
                shiroMapper.UpPermById(ids.get(i), new Integer(permid[i]));
            }
            for(int i = updateNum-1; i >= num; i--) {
                shiroMapper.addPermById(userid,new Integer(permid[i]) );
            }
        }
        else if (updateNum == num) {
                for (int i = 0; i < num; i++) {
                    shiroMapper.UpPermById(ids.get(i), new Integer(permid[i]));
                }
        }
        else if (updateNum < num) {
            for (int i = 0; i < updateNum; i++) {
                shiroMapper.UpPermById(ids.get(i), new Integer(permid[i]));
            }
            for (int i = num-1; i >= updateNum; i--) {
                shiroMapper.deleteMyPerByID(ids.get(i));
            }
        }
        //清除所有用户的权限缓存
        CommonController.clearAuth();

        }

        @Override
        public List<Resource> getPermissionByid (Integer id){
            //组权限
            List<Resource> permissions = shiroMapper.getPermissionByid(id);
            //个人权限
            List<Resource> permissions2 = shiroMapper.selectUserPermByid(id);
            //合并权限
            permissions.addAll(permissions2);
            //利用 TreeSet的排序去重构造函数来达到去重元素的目的
            //根据权限id去重
            List<Resource> distinctList = null;
            if (permissions.size() > 0) {
                distinctList = permissions.stream()
                        .collect(Collectors.collectingAndThen(Collectors.toCollection(
                                () -> new TreeSet<>(Comparator.comparing(Resource::getId))), ArrayList::new));
            } else {
                ExceptionCast.cast(CommonCode.UNAUTHORISE, "ShiroServiceImpl", "getPermissionByid");//此人没有任何权限
            }
            return distinctList;
        }

        @Override
        public List<Role> getRole () {
            List<Role> list = shiroMapper.selectRole();
            if (list.size() > 0) {
                for (Role role :
                        list) {
                    role.setNum(shiroMapper.getRoleNum(role.getId()));
                    System.out.println(role);
                }
            }
            return list;
        }

        @Transactional
        @Override
        public void addRole (String name,int array[]){
            Role role = new Role(name);
            shiroMapper.addRole(role);
            if (array != null) {
                Integer a = role.getId();
                for (Integer rid :
                        array) {
                    shiroMapper.addResource(a, rid);
                }
            } else {
                ExceptionCast.cast(CommonCode.DATA_FAIL, "ShiroServiceImpl", "addRole");
            }
        }

        @Transactional
        @Override
        public void updateRole (String name, Integer id,@RequestParam int[] array){
            shiroMapper.updateRoleName(name, id);
            int updateNum = array.length;
            List<Integer> ids = shiroMapper.findIdByRoleID(id);
            int num = ids.size();
            if (updateNum > num) {
                for (int i = 0; i < num; i++) {
                    shiroMapper.updateRoleResource(ids.get(i), new Integer(array[i]));
                }
                for(int i = updateNum-1; i >= num; i--) {
                    shiroMapper.addResource(id,new Integer(array[i]) );
                }
            }
            else if (updateNum == num) {
                for (int i = 0; i < num; i++) {
                    shiroMapper.updateRoleResource(ids.get(i), new Integer(array[i]));
                }
            }
            else if (updateNum < num) {
                for (int i = 0; i < updateNum; i++) {
                    shiroMapper.updateRoleResource(ids.get(i), new Integer(array[i]));
                }
                for (int i = num-1; i >= updateNum; i--) {
                    shiroMapper.deleteOneRoleResourceByID(ids.get(i));
                }
            }
            //清除所有用户的权限缓存
            CommonController.clearAuth();
        }

        @Override
        public Role getRoleByID (Integer roleid){
            Role role = new Role();
            List<Integer> PID = shiroMapper.getPermById(roleid);
            List<Resource> list;
            if (PID.size() > 0) {
                list = shiroMapper.getPermName(PID);
                role = shiroMapper.selectRoleByID(roleid);
                role.setResource(list);

            } else {
                ExceptionCast.cast(CommonCode.DATA_FAIL, "ShiroServiceImpl", "getRoleByID");
            }
            return role;
        }

        @Transactional
        @Override
        public void deleteRoleByID (Integer roleid){
            //删除用户与组的关联关系
             shiroMapper.deleteUserRoleByRoleID(roleid);
            //删除组与权限的关联关系
            shiroMapper.deleteRoleResourceByID(roleid);
            shiroMapper.deleteRoleByID(roleid);
        }

        @Override
        public List<UserRole> queryInRole (Integer roleid){
            return shiroMapper.queryInRole(roleid);
        }

        @Transactional
        @Override
        public void addManager (String userName, String userPhone, Integer roleid, Integer postid){
            //密码默认为手机号
            String userPwd = Md5Util.encodeByMd5(userPhone);
            Date now = new Date();
            //向manager表添加用户
            //先通过手机号回查用户是否存在
            Manager manager = shiroMapper.selectManagerByPhone(userPhone);
            if (manager != null) {
                ExceptionCast.cast(CommonCode.PHONE_REPEAT_REGISTER, "ShiroServiceImpl", "addManager");
            }
            Manager newManager = new Manager(userName, userPwd, userPhone, now);
            shiroMapper.addManager(newManager);
            //给用户关联角色
            shiroMapper.addUserRole(new Integer(newManager.getId()), roleid);
            //给用户添加职位
            shiroMapper.addPost(new Integer(newManager.getId()), postid);

        }

        /**
         * @Author 马志磊
         * @Description 修改密码
         * @Return a
         * @Date 2019/6/26 14:43
         */
        @Override
        public void updatePwdByPhone (String userPhone, String oldpwd, String newPwd) throws Exception {
            String Pwd = Md5Util.encodeByMd5(oldpwd);
            //先通过手机号回查用户是否存在
            Manager manager = shiroMapper.selectManagerByPhone(userPhone);

            if (Pwd.equals(manager.getUserpwd())) {
                String newpwd = Md5Util.encodeByMd5(newPwd);
                shiroMapper.updatePwd(userPhone, newpwd);
            } else {
                ExceptionCast.cast(CommonCode.DATA_FAIL, "ShiroServiceImpl", "updatePwdByPhone");
            }
        }

        @Override
        public List<Post> postList () {
            return shiroMapper.postList();
        }

        @Override
        public int addPost (String name){
            return shiroMapper.addPostname(name);
        }

        @Transactional
        @Override
        public void deleteManager (Integer id){
            //删除个人的权限关联关系
            shiroMapper.deleteUserResourceByManID(id);
            //删除用户表
             shiroMapper.deleteManager(id);
            //删除成员关联的职位
             shiroMapper.deleteManagerPost(id);
            //删除成员关联的角色
             shiroMapper.deleteManagerRole(id);
//            if (a + b + c != 3) {
//                ExceptionCast.cast(CommonCode.FAIL, "ShiroServiceImpl", "deleteManager"); //操作失败,事物回滚
//
//            }

        }

        @Override
        public void updatePost (@RequestBody Post post){
            shiroMapper.updatePost(post);
        }

        @Override
        public int deletePost (Integer id){
            return shiroMapper.deletePost(id);
        }
    }

4.Mapper

package com.longjie.yunxin.mapper;

import com.longjie.yunxin.pojo.*;
import io.lettuce.core.dynamic.annotation.Param;

import java.util.List;

public interface ShiroMapper {
    //根据手机号查询用户
    Manager selectManagerByPhone(String phone);

    //获取所有人的组权限
    List<Permission> selectPerm();

    //权限列表
    List<Permission> permissionList();

    //添加个人权限
    Integer addPermById(@Param("userid") Integer userid, @Param("permid") Integer permid);

    Integer UpPermById(@Param("id") Integer id, @Param("permid") Integer permid);

    //根据id查询个人分配权限
    List<Resource> selectUserPermByid(Integer id);

    //根据用户id获取个人的权限信息
    List<Resource> getPermissionByid(Integer id);

    //获取所有角色
    List<Role> selectRole();

    Role selectRoleByID(Integer id);

    //根据角色id获取该角色的人数
    Integer getRoleNum(Integer id);

    //增加分组(角色)信息
    Integer addRole(Role role);

    //修改分组信息
    Integer updateRoleName(@Param("name") String name, @Param("id") Integer id);

    //删除分组对应的权限
    Integer deleteRoleResourceByID(Integer roleid);

    //关联角色的资源
    Integer addResource(@Param("roleid") Integer roleid, @Param("resourceid") Integer resourceid);

    //根据roleid查询其权限id
    List<Integer> getPermById(Integer roleid);

    //查询角色名
    List<Resource> getPermName(List permid);

    //删除分组
    Integer deleteRoleByID(Integer roleid);

    //查询组内信息
    List<UserRole> queryInRole(Integer roleid);

    //向manager添加成员
    Integer addManager(Manager manager);

    //根据成员id,角色id关联角色
    Integer addUserRole(@Param("managerid") Integer managerid, @Param("roleid") Integer roleid);

    //根据成员id,职位id关联职位
    Integer addPost(@Param("managerid") Integer managerid, @Param("postid") Integer postid);

    //修该密码
    Integer updatePwd(@Param("userPhone") String userPhone, @Param("newPwd") String newPwd);

    //查询所有职位
    List<Post> postList();

    //增加职位
    Integer addPostname(String name);

    //删除组成员
    Integer deleteManager(Integer id);

    //删除成员关联的职位
    Integer deleteManagerPost(Integer id);

    //删除成员关联的角色
    Integer deleteManagerRole(Integer id);

    //修改职位
    Integer updatePost(Post post);

    //删除职位
    Integer deletePost(Integer id);

    //查询此用户的权限条数
    List<Integer> findIdByManageerID(Integer id);

    //根据id删除个人权限
    Integer deleteMyPerByID(Integer id);

    //根据组id查询组内权限条数
    List<Integer> findIdByRoleID(Integer id);

    //根据role_resource表id修改组权限
    void updateRoleResource(@Param("id") Integer id, @Param("resourceId") Integer resourceId);

    //根据role_resource表id删除
    void deleteOneRoleResourceByID(Integer id);

    //根据组ID查询组内成员ID
    Integer deleteUserRoleByRoleID(Integer roleId);

    //根据用户ID删除个人的权限关联关系
    Integer deleteUserResourceByManID(Integer ManagerID);
}

5.mapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.longjie.yunxin.mapper.ShiroMapper">

    <select id="selectManagerByPhone" resultType="com.longjie.yunxin.pojo.Manager">
        select * from manager where userPhone=#{phone}
    </select>

    <select id="selectPerm" resultType="com.longjie.yunxin.pojo.Permission">
     select u.id,u.username,u.userPhone,re.name,re.url from role r
      inner join user_role ur on ur.role_id=r.id
      inner join manager u on u.id=ur.user_id
      inner join role_resource rr on rr.role_id=r.id
      inner join resource re on re.id=rr.resource_id
    </select>
    <select id="permissionList" resultType="com.longjie.yunxin.pojo.Permission">
        select * from resource
    </select>
    <select id="selectUserPermByid" resultType="com.longjie.yunxin.pojo.Resource">
      select r.id,r.name,r.url,u.userName from user_resource ur
      inner join manager u on ur.user_id=u.id
      inner join resource r on ur.resource_id=r.id
      where u.id=#{id,jdbcType=INTEGER}
    </select>
    <select id="getPermissionByid" resultType="com.longjie.yunxin.pojo.Resource">
      select re.id,re.name,re.url ,u.userName from  role r
      inner join user_role ur on ur.role_id=r.id
      inner join manager u on u.id=ur.user_id
      inner join role_resource rr on rr.role_id=r.id
      inner join resource re on re.id=rr.resource_id
      where u.id=#{id,jdbcType=INTEGER}
    </select>
    <select id="addPermById" parameterType="java.lang.Integer">
      insert  into user_resource(user_id,resource_id) values(#{userid,jdbcType=INTEGER},#{permid,jdbcType=INTEGER})
    </select>

    <select id="UpPermById" parameterType="java.lang.Integer">
      update user_resource set resource_id=#{permid,jdbcType=INTEGER} where id=#{id,jdbcType=INTEGER}
    </select>

    <select id="selectRole" resultType="com.longjie.yunxin.pojo.Role">
        select * from role
    </select>

    <select id="selectRoleByID" resultType="com.longjie.yunxin.pojo.Role">
        select * from role where id=#{id,jdbcType=INTEGER}
    </select>

    <select id="getRoleNum" parameterType="java.lang.Integer" resultType="java.lang.Integer">
        select count(*) from user_role ur where ur.role_id = #{id,jdbcType=INTEGER}
    </select>

    <insert id="addRole" useGeneratedKeys="true" keyProperty="id" parameterType="com.longjie.yunxin.pojo.Role">
        insert into role(name) value(#{name})
    </insert>

    <update id="updateRoleName">
        update role set name=#{name} where id=#{id,jdbcType=INTEGER}
    </update>

    <insert id="addResource" parameterType="java.lang.Integer">
        insert into role_resource(role_id,resource_id) values(#{roleid},#{resourceid})
    </insert>

    <select id="getPermById" parameterType="java.lang.Integer" resultType="java.lang.Integer">
        select resource_id from role_resource rs where rs.role_id = #{roleid}
    </select>

    <select id="getPermName" parameterType="java.lang.Integer" resultType="com.longjie.yunxin.pojo.Resource">
        select * from resource r where r.id in
        <foreach item="permid" collection="list"
                 open="(" separator="," close=")">
            #{permid}
        </foreach>
    </select>

    <delete id="deleteRoleByID" parameterType="java.lang.Integer">
        delete from role where role.id=#{roleid}
    </delete>

    <select id="queryInRole" parameterType="java.lang.Integer" resultType="com.longjie.yunxin.pojo.UserRole">
        select u.id id,u.userName name,u.userPhone phone,p.postName postname, u.userCreatedTime userCreatedTime,ur.status status,r.name rolename,ur.isuse isuse
        from user_role ur
        inner join manager u on ur.user_id=u.id
        inner join role r on r.id=ur.role_id
        inner join manager_post mp  on mp.user_id=u.id
        inner join post p on mp.post_id=p.id
        where ur.role_id=#{roleid,jdbcType=INTEGER}
    </select>
    <insert id="addManager"  useGeneratedKeys="true" keyProperty="id" parameterType="com.longjie.yunxin.pojo.Manager">
        insert into manager(userName,userPwd,userPhone,userCreatedTime) values (#{username,jdbcType=VARCHAR},#{userpwd,jdbcType=VARCHAR},
        #{userphone,jdbcType=VARCHAR},#{usercreatedtime,jdbcType=DATE})
    </insert>
    <insert id="addUserRole">
        insert into user_role(user_id,role_id) values (#{managerid,jdbcType=INTEGER},#{roleid,jdbcType=INTEGER})
    </insert>
    <insert id="addPost">
        insert into manager_post(user_id,post_id) values (#{managerid,jdbcType=INTEGER},#{postid,jdbcType=INTEGER})
    </insert>
    <update id="updatePwd">
        update manager set userPwd=#{newPwd,jdbcType=VARCHAR} where userPhone=#{userPhone,jdbcType=VARCHAR}
    </update>
    <select id="postList" resultType="com.longjie.yunxin.pojo.Post">
        select * from post
    </select>
    <insert id="addPostname">
        insert into post(postName) values(#{name})
    </insert>
    <delete id="deleteManager" parameterType="java.lang.Integer">
        delete from manager where id=#{id,jdbcType=INTEGER}
    </delete>
    <delete id="deleteManagerPost" parameterType="java.lang.Integer">
        delete from manager_post where user_id=#{id,jdbcType=INTEGER}
    </delete>
    <delete id="deleteManagerRole" parameterType="java.lang.Integer">
        delete from user_role where user_id=#{id,jdbcType=INTEGER}
    </delete>
    <delete id="deleteRoleResourceByID" parameterType="java.lang.Integer">
        delete from role_resource where role_id =#{roleid,jdbcType=INTEGER}
    </delete>
    <update id="updatePost" parameterType="post">
        update post set postName =#{postName,jdbcType=VARCHAR} where id =#{id,jdbcType=INTEGER}
    </update>
    <delete id="deletePost" parameterType="java.lang.Integer">
        delete from post where id=#{id,jdbcType=INTEGER}
    </delete>
    <select id="findIdByManageerID" resultType="java.lang.Integer" parameterType="java.lang.Integer">
        select id from user_resource where user_id=#{id}
    </select>
    <delete id="deleteMyPerByID" parameterType="java.lang.Integer">
        delete from user_resource where id=#{id}
    </delete>
    <select id="findIdByRoleID" resultType="java.lang.Integer" parameterType="java.lang.Integer">
        select id from role_resource where role_id=#{id}
    </select>
    <update id="updateRoleResource" parameterType="java.lang.Integer">
       update role_resource set resource_id =#{resourceId,jdbcType=VARCHAR} where id =#{id,jdbcType=INTEGER}
    </update>
    <delete id="deleteOneRoleResourceByID" parameterType="java.lang.Integer">
        delete from role_resource where id=#{id}
    </delete>
    <select id="deleteUserRoleByRoleID" parameterType="java.lang.Integer">
        delete from user_role  where role_id=#{roleId}
    </select>
    <delete id="deleteUserResourceByManID" parameterType="java.lang.Integer" >
        delete from user_resource  where user_id=#{ManagerID}
    </delete>
</mapper>

权限部分到此完成!
<这个需求的实现是有限制的:个人用户的权限不能少于此人所在的组的组权限,简单说就是只能给单独用户赋权,不能减少权限>

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Spring Boot是一个用于快速开发Java应用程序的开源框架,Shiro是一个强大且易于使用的Java安全框架,Redis是一个开源的内存数据库。结合使用这些技术可以实现单点登录功能。 在Spring Boot中使用Shiro来处理认证和授权,可以通过配置Shiro的Realm来实现用户的登录认证和权限控制。将用户的信息存储在Redis中,利用Redis的持久化特性来实现用户登录状态的共享和存储。 首先,在Spring Boot项目的配置文件中配置Redis的连接信息,以便连接到Redis数据库。 然后,创建一个自定义的Shiro的Realm,在其中重写认证和授权的方法。在认证方法中,将用户的登录信息存储到Redis中,以便其他服务可以进行验证。在授权方法中,根据用户角色权限进行相应的授权操作。 接着,在Spring Boot项目的配置类中配置Shiro的相关设置,包括Realm、Session管理器、Cookie管理器等。 最后,可以在Controller层中使用Shiro的注解来标记需要进行认证和授权的接口,以确保只有登录后且具备相应权限用户才能访问这些接口。 总的来说,通过使用Spring BootShiroRedis的组合,可以实现单点登录的功能。用户在登录后,将登录信息存储到Redis中,其他服务可以通过验证Redis中的数据来判断用户的登录状态。同时,Shiro提供了强大的认证和授权功能,可以确保只有具备相应权限用户才能访问受保护的接口。这些功能的具体实现可以通过深入研究Spring BootShiroRedis的源码来了解。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

马瑾瑜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值