SpringBoot项目+Shiro(权限框架)+Redis(缓存)集成

项目是SpringCloud框架,分布式项目,包括Eureka、Zuul、Config、User-Svr(用户管理的服务,既是服务端也是客户端);

SpringCloud框架的SpringBoot 的项目搭建就不再赘述,这里重点介绍如何引入集成 Shiro 框架:

Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码学和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。

一、数据库设计

  这里数据库表为5个分别是: 用户表、角色表、权限表、用户角色关系表、角色权限资源关系表

遵循三步走:导包,配置,写代码

二、导包(引入依赖)

 <!-- shiro -->
      <dependency>
          <groupId>org.apache.shiro</groupId>
          <artifactId>shiro-ehcache</artifactId>
          <version>1.4.2</version>
      </dependency>

      <!-- https://mvnrepository.com/artifact/com.sun.xml.fastinfoset/FastInfoset -->
      <dependency>
          <groupId>org.apache.shiro</groupId>
          <artifactId>shiro-spring</artifactId>
          <scope>compile</scope>
          <version>1.4.2</version>
      </dependency>
      <!-- shiro+redis缓存插件 -->
      <dependency>
          <groupId>org.crazycake</groupId>
          <artifactId>shiro-redis</artifactId>
          <version>2.4.2.1-RELEASE</version>
          <scope>compile</scope>
      </dependency>

 三、创建ShiroConfig配置ShiroServerConfig、ShiroAnnotionConfig

package com.keenyoda.iot.microservice.shiroconfig;

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.shiro.web.session.mgt.DefaultWebSessionManager;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * Created by IntelliJ IDEA
 * 这是一个神奇的Class
 *
 * @author zhz
 * @date 2019/12/13 16:31
 */

@Configuration
public class ShiroServerConfig {

        @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        //访问的是后端url的地址,这里要写base 服务的公用登录接口。
        shiroFilterFactoryBean.setLoginUrl("http://localhost:18900/base/loginpage");

        // 登录成功后要跳转的链接;现在应该没用
        //shiroFilterFactoryBean.setSuccessUrl("/index");

        // 未授权界面;可以写个公用的403页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");

        // private Map<String, Filter> filters;  shiro有一些默认的拦截器 比如auth,它就是FormAuthenticationFilter表单拦截器  <取名,拦截器地址>,可以自定义拦截器放在这
        //private Map<String, String> filterChainDefinitionMap; <url,拦截器名>哪些路径会被此拦截器拦截到
        //Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();
        //AdminFilter ad=new AdminFilter();
        //filters.put("ad", ad);

        // 拦截器.
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        // 配置不会被拦截的链接 顺序判断
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/loginpage", "anon");
        filterChainDefinitionMap.put("/swagger-ui.html#", "anon");

        filterChainDefinitionMap.put("/base/test", "authc");

        // 配置退出过滤器,其中的具体的退出代码Shiro已经替我们实现了,加上这个会导致302,请求重置,暂不明白原因
        //filterChainDefinitionMap.put("/logout", "logout");
        //配置某个url需要某个权限码
        //filterChainDefinitionMap.put("/hello", "perms[how_are_you]");

        // 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边
        // <!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问;user:remember me的可以访问-->
//        filterChainDefinitionMap.put("/fine", "user");
        //filterChainDefinitionMap.put("/focus/**", "ad");

        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        System.out.println("Shiro拦截器工厂类注入成功");
        return shiroFilterFactoryBean;
    }

    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 设置realm.
        securityManager.setRealm(myShiroRealm());
        // 自定义缓存实现 使用redis
        securityManager.setCacheManager(cacheManager());
        // 自定义session管理 使用redis
        securityManager.setSessionManager(sessionManager());
        return securityManager;
    }

    /**
     * 身份认证realm; (这个需要自己写,账号密码校验;权限等)
     *
     * @return
     */
    @Bean
    public ShiroServerRealm myShiroRealm() {
        ShiroServerRealm myShiroRealm = new ShiroServerRealm();
        return myShiroRealm;
    }

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

    /**
     * 配置shiro redisManager
     * 使用的是shiro-redis开源插件
     *
     * @return
     */
    @Bean
    public RedisManager redisManager() {
        RedisManager redisManager = new MyRedisManager();
//        RedisManager redisManager = new RedisManager();
//        redisManager.setHost(host);
//        redisManager.setPort(port);
//        // 配置缓存过期时间
//        redisManager.setExpire(expireTime);
//        redisManager.setTimeout(timeOut);
        // redisManager.setPassword(password);
        return redisManager;
    }

//    /**
//     * 配置shiro redisManager
//     * 网上的一个 shiro-redis 插件,实现了shiro的cache接口、CacheManager接口就
//     * @return
//     */
//    @Bean
//    public RedisManager redisManager() {
//        RedisManager redisManager = new RedisManager();
//        redisManager.setHost("localhost");
//        redisManager.setPort(6379);
//        redisManager.setExpire(18000);// 配置过期时间
//        // redisManager.setTimeout(timeout);
//        // redisManager.setPassword(password);
//        return redisManager;
//    }

    /**
     * Session Manager
     * 使用的是shiro-redis开源插件
     */
    @Bean
    public DefaultWebSessionManager sessionManager() {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionDAO(redisSessionDAO());
        return sessionManager;
    }

    /**
     * RedisSessionDAO shiro sessionDao层的实现 通过redis
     * 使用的是shiro-redis开源插件
     */
    @Bean
    public RedisSessionDAO redisSessionDAO() {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        return redisSessionDAO;
    }

//    /**
//     * 限制同一账号登录同时登录人数控制
//     *
//     * @return
//     */
//    @Bean
//    public KickoutSessionControlFilter kickoutSessionControlFilter() {
//        KickoutSessionControlFilter kickoutSessionControlFilter = new KickoutSessionControlFilter();
//        kickoutSessionControlFilter.setCacheManager(cacheManager());
//        kickoutSessionControlFilter.setSessionManager(sessionManager());
//        kickoutSessionControlFilter.setKickoutAfter(false);
//        kickoutSessionControlFilter.setMaxSession(1);
//        kickoutSessionControlFilter.setKickoutUrl("/auth/kickout");
//        return kickoutSessionControlFilter;
//    }

    /***
     * 授权所用配置
     *
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }

    /***
     * 使授权注解起作用不如不想配置可以在pom文件中加入
     * <dependency>
     *<groupId>org.springframework.boot</groupId>
     *<artifactId>spring-boot-starter-aop</artifactId>
     *</dependency>
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

}
package com.keenyoda.iot.microservice.shiroconfig;

import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

@Configuration
public class ShiroAnnotionConfig {

      /**
     * Shiro生命周期处理器
     * @return
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
        return new LifecycleBeanPostProcessor();
    }
    /**
     * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
     * @return
     */
    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }
}

四、自定义Realm  ShiroServerRealm

package com.keenyoda.iot.microservice.shiroconfig;

import com.keenyoda.iot.microservice.userservice.PrivilegeService;
import com.keenyoda.iot.microservice.userservice.UserService;
import com.keenyoda.iot.pojos.rbac.ResourceVo;
import com.keenyoda.iot.pojos.user.UserEntity;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.List;

/**
 * Created by IntelliJ IDEA
 * 这是一个神奇的Class
 *
 * @author zhz
 * @date 2019/12/13 16:31
 */
public class ShiroServerRealm extends AuthorizingRealm {

    Boolean cachingEnabled=true;

    @Autowired
    private PrivilegeService privilegeService;

    @Autowired
    private UserService userService;

    /**
     * 1.授权方法,在请求需要操作码的接口时会执行此方法。不需要操作码的接口不会执行
     * 2.实际上是 先执行 AuthorizingRealm,自定义realm的父类中的 getAuthorizationInfo方法,
     * 逻辑是先判断缓存中是否有用户的授权信息(用户拥有的操作码),如果有 就直返回不调用自定义 realm的授权方法了,
     * 如果没缓存,再调用自定义realm,去数据库查询。
     * 用库查询一次过后,如果 在安全管理器中注入了 缓存,授权信息就会自动保存在缓存中,下一次调用需要操作码的接口时,
     * 就肯定不会再调用自定义realm授权方法了。   网上有分析AuthorizingRealm,shiro使用缓存的过程
     * 3.AuthorizingRealm 有多个实现类realm,推测可能是把 自定义realm注入了安全管理器,所以才调用自定义的
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo simpleAuthorInfo = new SimpleAuthorizationInfo();

        UserEntity userEntity=(UserEntity) principals.getPrimaryPrincipal();

        List<ResourceVo> resourceVos = privilegeService.selectResourceVoListByUserId(userEntity.getId());
        if(resourceVos!=null){
            for (ResourceVo resourceVo:resourceVos) {
                simpleAuthorInfo.addStringPermission(resourceVo.getResource());
            }
        }
        return simpleAuthorInfo;
    }

    /**
     * 1.和授权方法一样,AuthenticatingRealm的getAuthenticationInfo,先判断缓存是否有认证信息,没有就调用
     * 但试验,登录之后,再次登录,发现还是调用了认证方法,说明第一次认证登录时,没有将认证信息存到缓存中。不像授权信息,
     * 将缓存注入安全管理器,就自动保存了授权信息。 难道无法 防止故意多次登录 ,按理说不应该啊?
     * 2  可以在登录controller简单用session是否有key 判断是否登录?
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken authcToken) throws AuthenticationException {
        //获取基于用户名和密码的令牌
        //实际上这个authcToken是从LoginController里面currentUser.login(token)传过来的
        UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
        String account = token.getUsername();
        UserEntity user = userService.findUserUserId(account);
        if(user==null){throw new AuthenticationException("用户不存在");}

        //进行认证,将正确数据给shiro处理
        //密码不用自己比对,AuthenticationInfo认证信息对象,一个接口,new他的实现类对象SimpleAuthenticationInfo
        /*    第一个参数随便放,可以放user对象,程序可在任意位置获取 放入的对象
         * 第二个参数必须放密码,
         * 第三个参数放 当前realm的名字,因为可能有多个realm*/
        UserEntity baseUserVM = EntityUtils.entity2VM(user, UserEntity.class, "");
        SimpleAuthenticationInfo authcInfo=new SimpleAuthenticationInfo(baseUserVM, user.getPwd(), this.getName());

        //密码凭证器加盐
        authcInfo.setCredentialsSalt(ByteSource.Util.bytes(user.getId()));
        //清缓存中的授权信息,保证每次登陆 都可以重新授权。因为AuthorizingRealm会先检查缓存有没有 授权信息,再调用授权方法
        super.clearCachedAuthorizationInfo(authcInfo.getPrincipals());

        return authcInfo;
        //返回给安全管理器,securityManager,由securityManager比对数据库查询出的密码和页面提交的密码
        //如果有问题,向上抛异常,一直抛到控制器
    }
}

工具类

package com.keenyoda.iot.microservice.shiroconfig;

import com.github.pagehelper.Page;
import org.springframework.beans.BeanUtils;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class EntityUtils {

    /**
     * 实体列表转Vm
     *
     * @param source           原列表
     * @param vmClass          vm类
     * @param ignoreProperties 忽略的字段
     * @param <T>              泛型
     * @return vm列表
     */
    public static <T> List<T> entity2VMList(List<?> source, Class<T> vmClass, String... ignoreProperties) {
        List<T> target = (source instanceof Page ? new Page<T>() : new ArrayList<T>());
        if (source instanceof Page) {
            BeanUtils.copyProperties(source, target);
        }
        if (CollectionUtils.isEmpty(source)) {
            return target;
        }
        source.forEach(e -> {
            target.add(entity2VM(e, vmClass, ignoreProperties));
        });
        return target;
    }

    /**
     * 实体转VM
     *
     * @param source           原对象
     * @param vmClass          要转换的对象
     * @param ignoreProperties 忽略的属性
     * @param <T>              泛型
     * @return 转换后对象
     * @author Say
     */
    public static <T> T entity2VM(Object source, Class<T> vmClass, String... ignoreProperties) {
        if (null == source) {
            return null;
        }
        try {
            T target = vmClass.newInstance();
            BeanUtils.copyProperties(source, target, ignoreProperties);
            return target;
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * VM转实体
     * 底层用的vm2Entity,只是方法名做区分
     *
     * @param source           vm
     * @param entClass         实体
     * @param ignoreProperties 忽略的属性
     * @param <T>              泛型
     * @return 转换后的对象
     * @author Say
     */
    public static <T> T vm2Entity(Object source, Class<T> entClass, String... ignoreProperties) {
        return entity2VM(source, entClass, ignoreProperties);
    }

    /**
     * VM转实体集合
     * 底层用的entity2VMList,只是方法名做区分
     *
     * @param source           原对象
     * @param entClass         实体
     * @param ignoreProperties 忽略的属性
     * @param <T>              泛型
     * @return 转换后的对象
     * @author Say
     */
    public static <T> List<T> vm2EntityList(List<?> source, Class<T> entClass, String... ignoreProperties) {
        return entity2VMList(source, entClass, ignoreProperties);
    }

    /**
     * Entity VM 互转
     *
     * @param object      数据源
     * @param laterObject 转换对象
     * @param <T>         泛型
     */
    public static <T> void copyProperties(final T object, T laterObject) {

        if (null == object || null == laterObject) {
            return;
        }

        ConcurrentHashMap<String, Method> getMethods = findGetMethods(object.getClass().getMethods());

        ConcurrentHashMap<String, Method> setMethods = findSetMethods(laterObject.getClass().getDeclaredMethods());

        Iterator<Map.Entry<String, Method>> iterator = getMethods.entrySet().iterator();

        while (iterator.hasNext()) {
            Map.Entry<String, Method> entry = iterator.next();
            String methodName = entry.getKey();
            Method getMethod = entry.getValue();
            Method setMethod = setMethods.get(methodName);
            if (null == setMethod) {
                continue;
            }
            try {
                Object value = getMethod.invoke(object, new Object[]{});
                setMethod.invoke(laterObject, value);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 获取所有的get方法
     *
     * @param methods 所有的方法
     * @return 所有的get方法
     */
    private static ConcurrentHashMap<String, Method> findGetMethods(Method[] methods) {
        ConcurrentHashMap<String, Method> getMethodsMap = new ConcurrentHashMap<>();
        for (Method method : methods) {
            if (isGetMethod(method.getName())) {
                getMethodsMap.put(getMethodName(method.getName()), method);
            }
        }
        return getMethodsMap;
    }

    /**
     * 获取所有的set方法
     *
     * @param methods 所有的方法
     * @return 所有的set方法
     */
    private static ConcurrentHashMap<String, Method> findSetMethods(Method[] methods) {
        ConcurrentHashMap<String, Method> setMethodsMap = new ConcurrentHashMap<>();
        for (Method method : methods) {
            if (isSetMethod(method.getName())) {
                setMethodsMap.put(getMethodName(method.getName()), method);
            }
        }
        return setMethodsMap;
    }

    /**
     * 取方法名
     *
     * @param getMethodName 方法名称
     * @return 去掉get set的方法名
     */
    private static String getMethodName(String getMethodName) {
        String fieldName = getMethodName.substring(3, getMethodName.length());
        return fieldName;
    }

    /**
     * 判断是否是get方法
     *
     * @param methodName
     * @return
     */
    private static boolean isGetMethod(String methodName) {
        int index = methodName.indexOf("get");
        if (index == 0) {
            return true;
        }
        return false;
    }

    /**
     * 判断是否是set方法
     *
     * @param methodName 方法名
     * @return 是否为set 方法
     */
    private static boolean isSetMethod(String methodName) {
        int index = methodName.indexOf("set");
        if (index == 0) {
            return true;
        }
        return false;
    }

}

五、异常处理类,拦截未授权页面(未授权页面有三种实现方式,我这里使用异常处理)

package com.keenyoda.iot.microservice.shiroconfig;

import com.keenyoda.iot.commons.Message;
import com.keenyoda.iot.commons.enumpackage.ErrorCodeEnum;
import org.apache.shiro.authz.AuthorizationException;
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.HttpServletRequest;

/**
 * Created by IntelliJ IDEA
 * 这是一个神奇的Class
 * 全局捕捉无权限异常
 *
 * @author zhz
 * @date 2019/12/13 15:40
 */
@ControllerAdvice
public class GlobalDefaultExceptionHandler {

    @ExceptionHandler(UnauthorizedException.class)
    @ResponseBody
    public Message defaultExceptionHandler(HttpServletRequest req,Exception e){

        return new Message(ErrorCodeEnum.UNAUTHORIZED.getValue(),"对不起,你没有访问权限!");
    }

    @ExceptionHandler(AuthorizationException.class)
    @ResponseBody
    public Message throwAuthenticationException(HttpServletRequest req,Exception e){
        return new Message(ErrorCodeEnum.AUTHENTICATION_EXCEPTION.getValue(),"账号验证异常,请重新登录!");
    }
}

六、因为不想我这里把redis单独做成了一个服务,为了不用多次配置,重写RedisManager 中的两个方法

package com.keenyoda.iot.microservice.shiroconfig;

import com.keenyoda.iot.microservice.redisservice.RedisService;
import org.crazycake.shiro.RedisManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Base64;

/**
 * Created by IntelliJ IDEA
 * 这是一个神奇的Class
 *
 * @author zhz
 * @date 2019/12/13 16:31
 */
@Component
public class MyRedisManager extends RedisManager {

    @Autowired
    RedisService redisService;

    @Override
    public byte[] set(byte[] key, byte[] value, int expire) {
        String val = Base64.getEncoder().encodeToString(value);
        expire=12000;
        redisService.set(new String(key),val,expire);
        return value;
    }

    @Override
    public byte[] get(byte[] key){
        String s = redisService.get(new String(key));
        if (s == null){
            return null;
        }
        return Base64.getDecoder().decode(s);
    }

    public static void main(String[] args) {
        String a = null;
        System.out.println(Base64.getDecoder().decode(a));
    }
}

七、登录部分代码

/**
     * 用户登录
     * zhz
     *
     * @param loginUser
     */
    @RequestMapping("login")
    @ResponseBody
    public Message<String> login(LoginUserVM loginUser) throws IncorrectCredentialsException {
        Asserts.notEmpty(loginUser,"登录用户不能为空");
        String account=loginUser.getLoginName();
        String password=loginUser.getPassword();
        UsernamePasswordToken token = new UsernamePasswordToken(account,password,false);
        token.setRememberMe(true);

        Subject currentUser = SecurityUtils.getSubject();
        try {
            currentUser.login(token);
        } catch(IncorrectCredentialsException e){
            return Message.ok("密码错误",500);
        } catch (AuthenticationException e) {
//            return Message.ok("登录失败");
            return Message.ok(e.getMessage(),500);
        }

        return Message.ok(FocusMicroBaseConstants.SUCCESS);
    }
 private Message getUserToken(UserEntity userEntity, UserInfo userInfo) {
        UsernamePasswordToken userToken = new UsernamePasswordToken(userEntity.getId(), userEntity.getPwd(), false);
        userToken.setRememberMe(true);
        Subject currentUser = SecurityUtils.getSubject();
        try {
            currentUser.login(userToken);
        } catch (IncorrectCredentialsException e) {
            return new Message(ErrorCodeEnum.PARAM_ERROR.getValue(), "密码错误");
        } catch (AuthenticationException e) {
            return new Message(ErrorCodeEnum.FAILED.getValue(), "failed");
        }
        return new Message(ErrorCodeEnum.SUCCESS.getValue(), userInfo);
    }

感谢几位大牛提供的详细介绍

参考 https://www.iteye.com/blog/jinnianshilongnian-2049092;

https://blog.csdn.net/u014203449/article/details/88087516

https://www.cnblogs.com/xifengxiaoma/p/9569793.html

https://www.cnblogs.com/caichaoqi/p/8900677.html

原文地址:https://www.cnblogs.com/wdzhz/p/12051202.html

时间: 2024-08-30 03:37:00

SpringBoot项目+Shiro(权限框架)+Redis(缓存)集成的相关文章

(转) shiro权限框架详解06-shiro与web项目整合(上)

http://blog.csdn.net/facekbook/article/details/54947730 shiro和web项目整合,实现类似真实项目的应用 本文中使用的项目架构是springMVC+mybatis,所以我们是基于搭建好的项目进行改造的. 将shiro整合到web应用中 登录 退出 认证信息在页面展现,也就是显示菜单 shiro的过滤器 将shiro整合到web应用中 数据库脚步 sql脚步放到项目中,项目上传到共享的资源中,文章最后给出共享url. 去除项目中不使用shi

Shiro权限框架简介

http://blog.csdn.net/xiaoxian8023/article/details/17892041 Shiro权限框架简介 2014-01-05 23:51 3111人阅读 评论(37) 收藏 举报  分类: [java框架](25)  版权声明:本文为博主原创文章,未经博主允许不得转载.如需转载请声明:[转自 http://blog.csdn.net/xiaoxian8023 ] 目录(?)[+] 最近加入了gxpt项目组,被安排做权限模块,所以也有幸第一次接触到了Shiro

shiro权限框架与spring框架轻松整合

2017年06月26日 17:53:30 阅读数:419 shiro是一个权限框架,用于管理网站的权限,大到网站登录过滤,小到一个菜单或按钮是否显示,shiro学习起来非常简单,以下是shiro的执行流程图: 看完不懂的请下载shiro全套视频教程: http://pan.baidu.com/s/1jHOX2MM Subject为当前用户,当它访问系统的时候,就会经过SecurityManager安全管理器,安全管理器类似一个中转站,它实际上会让Realm类来处理用户的认证和授权信息,认证和授权

在前后端分离的SpringBoot项目中集成Shiro权限框架

项目背景 公司在几年前就采用了前后端分离的开发模式,前端所有请求都使用ajax.这样的项目结构在与CAS单点登录等权限管理框架集成时遇到了很多问题,使得权限部分的代码冗长丑陋,CAS的各种重定向也使得用户体验很差,在前端使用vue-router管理页面跳转时,问题更加尖锐.于是我就在寻找一个解决方案,这个方案应该对代码的侵入较少,开发速度快,实现优雅.最近无意中看到springboot与shiro框架集成的文章,在了解了springboot以及shiro的发展状况,并学习了使用方法后,开始在网上

SpringMVC整合Shiro权限框架

尊重原创:http://blog.csdn.net/donggua3694857/article/details/52157313 最近在学习Shiro,首先非常感谢开涛大神的<跟我学Shiro>系列,在我学习的过程中发挥了很大的指导作用.学习一个新的东西首先就是做一个demo,多看不如多敲,只有在实践中才能发现自己的欠缺,下面记录下来我整合shiro的过程.如果有不足之处,还望各位看官多多指出. 一.基本名词解释 Apache Shiro是一个强大易用的Java安全框架.它可以帮助我们完成:

shiro权限框架实战

shiro框架作为一种开源的权限框架,通过将身份认证和授权从具体的业务逻辑中分离出来极大地提高了我们的开发速度,它的易用性使得它越来越受到人们的青睐.与之前的ACL权限框架相比,shiro能更容易的实现权限控制,而且作为基于RBAC的权限管理框架通过与shiro标签结合使用,能够让开发人员在更加细粒度的层面上进行控制.举个例子来讲,之前我们使用基于ACL的权限控制大多是控制到连接(这里的连接大家可以简单的认为是页面,下同)层面,也就是通过给用户授权让这个用户对某些连接拥有权限,这种情况显然不太适

shiro权限框架

权限的组成部分:用户 资源 角色 权限 数据库关系表设计是根据自己项目需求设计的 account表role表(id,rolename)account_role(id,aid,rid)permission(id,pername)role_permission(id,rid,pid) 没有设置用户和权限的关系,我们可以认为用户的权限是通过角色来决定的 1.导入jar包 shiro-all-1.2.1.jar 2.配置web.xml <!-- 权限过滤器--> <filter> <

Shiro 权限框架使用总结

我们首先了解下什么是shiro ,Shiro 是 JAVA 世界中新近出现的权限框架,较之 JAAS 和 Spring Security,Shiro 在保持强大功能的同时,还在简单性和灵活性方面拥有巨大优势 Shiro 是一个强大而灵活的开源安全框架,能够非常清晰的处理认证.授权.管理会话以及密码加密.如下是它所具有的特点: 易于理解的 Java Security API: 简单的身份认证(登录),支持多种数据源(LDAP,JDBC,Kerberos,ActiveDirectory 等): 对角

springboot配置shiro权限管理,网搜搜采集网站权限控制代码

import outshine.shiro.authc.AccountSubjectFactory; import outshine.shiro.filter.AuthenticatedFilter; import outshine.shiro.realm.AccountRealm; import org.apache.shiro.cache.CacheManager; import org.apache.shiro.cache.ehcache.EhCacheManager; import or