SpringBoot使用shiro-ehcache缓存

由于网上教程很多,对于shiro的概念和用法已经讲解非常详细,这里直接给出介绍shiro概念的博主连接Shiro笔记(一)----Shiro安全框架简介
以及写的不错的博客:springboot(十四):springboot整合shiro-登录认证和权限管理
SpringBoot整合Shiro
springboot整合shiro-ehcache不错的博文:springboot整合shiro-ehcache缓存(五)
这里是本项目源码,已经上传github,sql文件在doc文件夹里面,感兴趣的小伙伴可以下载 : 去下载
话不多说,先上pom文件:



    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.1.4.RELEASE
         
    
    com.example
    springboot-shiro
    0.0.1-SNAPSHOT
    springboot-shiro
    Demo project for Spring Boot

    
        1.8
    

    
        
            org.springframework.boot
            spring-boot-starter-thymeleaf
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.mybatis.spring.boot
            mybatis-spring-boot-starter
            2.0.1
        

        
            mysql
            mysql-connector-java
            runtime
        
        
            org.projectlombok
            lombok
            true
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
        
            
            
            
        
        
        
            org.apache.shiro
            shiro-spring
            1.4.0
        
        
        
            cn.hutool
            hutool-all
            4.5.7
        

        
        
            org.apache.shiro
            shiro-ehcache
            1.4.0
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
            
                
                org.mybatis.generator
                mybatis-generator-maven-plugin
                1.3.7
                
                    
                    src/main/resources/mybatis-generator-config.xml
                    true
                    true
                
                
                    
                        org.mybatis.generator
                        mybatis-generator-core
                        1.3.7
                    
                
            
        
    



数据库文件:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for permission
-- ----------------------------
DROP TABLE IF EXISTS `permission`;
CREATE TABLE `permission`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '权限id',
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '权限名称',
  `permission` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '权限',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of permission
-- ----------------------------
INSERT INTO `permission` VALUES (1, '用户管理', 'user:list');
INSERT INTO `permission` VALUES (2, '用户添加/更新', 'user:add');
INSERT INTO `permission` VALUES (3, '用户删除', 'user:delete');
INSERT INTO `permission` VALUES (4, '用户编辑页面', 'user:edit');
INSERT INTO `permission` VALUES (5, '文章管理', 'post:list');
INSERT INTO `permission` VALUES (6, '文章查询', 'post:search');
INSERT INTO `permission` VALUES (7, '创建文章页面', 'post:new');
INSERT INTO `permission` VALUES (8, '添加/修改文章', 'post:save');
INSERT INTO `permission` VALUES (9, '编辑文章页面', 'post:edit');

-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '角色id',
  `role` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '角色',
  `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '角色描述',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES (1, 'admin', '管理员');
INSERT INTO `role` VALUES (2, 'author', '作者');
INSERT INTO `role` VALUES (3, 'subscriber', '订阅者');

-- ----------------------------
-- Table structure for role_permission_ref
-- ----------------------------
DROP TABLE IF EXISTS `role_permission_ref`;
CREATE TABLE `role_permission_ref`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '关联表role_permission_id',
  `role_id` int(11) NOT NULL COMMENT '角色id',
  `permission_id` int(11) NOT NULL COMMENT '权限id',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 10 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of role_permission_ref
-- ----------------------------
INSERT INTO `role_permission_ref` VALUES (1, 1, 1);
INSERT INTO `role_permission_ref` VALUES (2, 1, 2);
INSERT INTO `role_permission_ref` VALUES (3, 1, 3);
INSERT INTO `role_permission_ref` VALUES (4, 1, 4);
INSERT INTO `role_permission_ref` VALUES (5, 1, 5);
INSERT INTO `role_permission_ref` VALUES (6, 1, 6);
INSERT INTO `role_permission_ref` VALUES (7, 1, 7);
INSERT INTO `role_permission_ref` VALUES (8, 1, 8);
INSERT INTO `role_permission_ref` VALUES (9, 1, 9);
INSERT INTO `role_permission_ref` VALUES (10, 2, 1);

-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户id',
  `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户名',
  `password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '密码',
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '姓名',
  `salt` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '盐值',
  `email` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '邮箱',
  `create_time` timestamp(0) NOT NULL ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '创建时间',
  `update_time` timestamp(0) NOT NULL ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES (1, 'tom', '9ade9a62fe19ed021b87f8dff0236ad7', '汤姆', 'n3R2XG', '[email protected]', '2019-05-05 10:38:49', '2019-05-05 10:38:49');
INSERT INTO `user` VALUES (2, 'cat', '5b345ca8ab78e8728cc8448203246496', 'CAT', '1gvEVM', '[email protected]', '2019-05-05 10:39:11', '2019-05-05 10:39:11');

-- ----------------------------
-- Table structure for user_role_ref
-- ----------------------------
DROP TABLE IF EXISTS `user_role_ref`;
CREATE TABLE `user_role_ref`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '关联表user_role_id',
  `user_id` int(11) NOT NULL COMMENT '用户id',
  `role_id` int(11) NOT NULL COMMENT '角色id',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user_role_ref
-- ----------------------------
INSERT INTO `user_role_ref` VALUES (1, 1, 1);
INSERT INTO `user_role_ref` VALUES (2, 1, 2);
INSERT INTO `user_role_ref` VALUES (3, 1, 3);
INSERT INTO `user_role_ref` VALUES (4, 2, 2);

SET FOREIGN_KEY_CHECKS = 1;

Shiro配置文件

package com.example.springbootshiro.shiro;

import lombok.extern.log4j.Log4j2;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.codec.Base64;
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.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

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

@Configuration
@Log4j2
public class ShiroConfig {
    /**
     * ShiroFilterFactoryBean
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager){
        log.info("进入【shiroFilter】");
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);

        //拦截器
        Map filterChainDefinitionMap = new LinkedHashMap<>();

        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/user/login", "anon");
        filterChainDefinitionMap.put("/user/logout", "logout");
        //根据用户的角色赋予相应的权限
//        filterChainDefinitionMap.put("/add", "roles[admin]");
//        filterChainDefinitionMap.put("/delete", "roles[admin]");
//        filterChainDefinitionMap.put("/delete", "roles[author]");
        filterChainDefinitionMap.put("/addPermission", "roles[author]");
        filterChainDefinitionMap.put("/add", "perms[user:add]");
        filterChainDefinitionMap.put("/delete", "perms[user:delete]");
        filterChainDefinitionMap.put("/userList", "perms[user:list]");
        //   /** 匹配所有的路径
        //  通过Map集合组成了一个拦截器链 ,自顶向下过滤,一旦匹配,则不再执行下面的过滤
        //  如果下面的定义与上面冲突,那按照了谁先定义谁说了算
        //  /** 一定要配置在最后
        filterChainDefinitionMap.put("/**", "authc");
        // 将拦截器链设置到shiro中
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 登录成功后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/index");
        //未授权页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");

        return shiroFilterFactoryBean;
    }


    /**
     * securityManager
     */

    @Bean(name = "securityManager")
    public SecurityManager securityManager(
            @Qualifier("myShiroRealm") MyShiroRealm myShiroRealm,
            @Qualifier("rememberMeManager") CookieRememberMeManager rememberMeManager,
//            @Qualifier("cacheManager") CacheManager cacheManager,
            @Qualifier("ehCacheManager")EhCacheManager ehCacheManager){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//        securityManager.setCacheManager(cacheManager);
        securityManager.setCacheManager(ehCacheManager);
        securityManager.setRememberMeManager(rememberMeManager);
        securityManager.setRealm(myShiroRealm);
        return securityManager;
    }

    /**
     * shiroRealm
     */

    @Bean(name = "myShiroRealm")
    public MyShiroRealm myShiroRealm(@Qualifier("hashedCredentialsMatcher") HashedCredentialsMatcher hashedCredentialsMatcher){
        MyShiroRealm myShiroRealm = new MyShiroRealm();
        myShiroRealm.setCachingEnabled(true);
        //启用身份验证缓存,即缓存AuthenticationInfo信息,默认false
        myShiroRealm.setAuthenticationCachingEnabled(true);
        //缓存AuthenticationInfo信息的缓存名称 在ehcache-shiro.xml中有对应缓存的配置
        myShiroRealm.setAuthenticationCacheName("authenticationCache");
        //启用授权缓存,即缓存AuthorizationInfo信息,默认false
        myShiroRealm.setAuthorizationCachingEnabled(true);
        //缓存AuthorizationInfo信息的缓存名称  在ehcache-shiro.xml中有对应缓存的配置
        myShiroRealm.setAuthorizationCacheName("authorizationCache");
        myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher);
        return new MyShiroRealm();
    }

    /**
     * 密码匹配凭证管理器
     *
     * @return
     */
    @Bean(name = "hashedCredentialsMatcher")
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        log.info("hashedCredentialsMatcher()");
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();

        hashedCredentialsMatcher.setHashAlgorithmName("MD5");// 散列算法:这里使用MD5算法;
        hashedCredentialsMatcher.setHashIterations(1024);// 散列的次数,比如散列两次,相当于md5(md5(""));

        return hashedCredentialsMatcher;
    }

    /**
     * cookie对象
     * @return
     */
    @Bean(name = "rememberMeCookie")
    public SimpleCookie rememberMeCookie(){
        SimpleCookie simpleCookie = new SimpleCookie("rememberMe");

        simpleCookie.setHttpOnly(true);
        //设置有效期时间30天
        simpleCookie.setMaxAge(259200);
        return simpleCookie;
    }

    @Bean(name = "rememberMeManager")
    public CookieRememberMeManager rememberMeManager(@Qualifier("rememberMeCookie")SimpleCookie rememberMeCookie){

        CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
        cookieRememberMeManager.setCookie(rememberMeCookie);
        //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
        cookieRememberMeManager.setCipherKey(Base64.decode("2AvVhdsgUs0FSA3SDFAdag=="));
        return cookieRememberMeManager;
    }

    /**
     * 开启shiro aop注解支持
     * 使用代理方式;所以需要开启代码支持
     * @param securityManager
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
    /**
     * 开启cglib代理
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
        creator.setProxyTargetClass(true);
        return creator;
    }

    //  缓存配置
    //shiro自带的MemoryConstrainedCacheManager作缓存
    // 但是只能用于本机,在集群时就无法使用,需要使用ehcache
    @Bean(name = "cacheManager")
    public CacheManager cacheManager() {
        MemoryConstrainedCacheManager cacheManager=new MemoryConstrainedCacheManager();//使用内存缓存
        return cacheManager;
    }

    //配置ehcache
    @Bean(name = "ehCacheManager")
    public EhCacheManager ehCacheManager(){
        EhCacheManager ehCacheManager = new EhCacheManager();
        ehCacheManager.setCacheManagerConfigFile("classpath:config/ehcache-shiro.xml");
        return ehCacheManager;
    }

    //在用spring管理我们的类的时候有时候希望有些属性值是来源于一些配置文件,系统属性,或者一些方法调用的结果,
    // 对于前两种使用方式可以使用spring的PropertyPlaceholderConfigurer类来注入,
    // 对于后一种则可以使用org.springframework.beans.factory.config.MethodInvokingFactoryBean类来生成需要注入的bean的属性。
    // 通过MethodInvokingFactory Bean类,可注入方法返回值。
    // MethodInvokingFactoryBean用来获得某个方法的返回值,该方法既可以是静态方法,也可以是实例方法。
    // 该方法的返回值可以注入bean实例属性,也可以直接定义成bean实例
    //可查看http://blog.sina.com.cn/s/blog_72ef7bea0102wa0v.html
    /**
     * 让某个实例的某个方法的返回值注入为Bean的实例
     * Spring静态注入
     * @param myShiroRealm
     * @param rememberMeManager
     * @param ehCacheManager
     * @return
     */
    @Bean(name = "methodInvokingFactoryBean")
    public MethodInvokingFactoryBean methodInvokingFactoryBean(
            @Qualifier("myShiroRealm") MyShiroRealm myShiroRealm,
            @Qualifier("rememberMeManager") CookieRememberMeManager rememberMeManager,
            @Qualifier("ehCacheManager")EhCacheManager ehCacheManager){
        MethodInvokingFactoryBean factoryBean = new MethodInvokingFactoryBean();
        factoryBean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
        factoryBean.setArguments(new Object[]{securityManager(myShiroRealm, rememberMeManager,ehCacheManager)});
        return factoryBean;
    }
}

自定义Realm:

package com.example.springbootshiro.shiro;

import cn.hutool.core.lang.Validator;
import com.example.springbootshiro.entity.Permission;
import com.example.springbootshiro.entity.Role;
import com.example.springbootshiro.entity.User;
import com.example.springbootshiro.service.PermissionService;
import com.example.springbootshiro.service.RoleService;
import com.example.springbootshiro.service.UserService;
import lombok.extern.log4j.Log4j2;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.SimpleHash;
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;

@Log4j2
public class MyShiroRealm extends AuthorizingRealm {

    @Autowired
    private UserService userService;

    @Autowired
    private RoleService roleService;

    @Autowired
    private PermissionService permissionService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        log.info("进入授权【doGetAuthorizationInfo】");
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        User user = (User) principals.getPrimaryPrincipal();
        List roleList = roleService.getRoleListByUserId(user.getId());
        for (Role role : roleList) {
                //添加角色
                authorizationInfo.addRole(role.getRole());
                List permissionList = permissionService.getPermissionListByRoleId(role.getId());
                for (Permission permission : permissionList) {
                    //添加权限
                    authorizationInfo.addStringPermission(permission.getPermission());
                }
        }
        return authorizationInfo;
    }


    /**
     * 登录认证
     * @param authenticationToken
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        //加入这一步的目的是在post请求的时候会先认证,然后再请求
        if (authenticationToken.getPrincipal() == null){
            return null;
        }
        log.info("进入认证【doGetAuthenticationInfo】");
        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
        String username = token.getUsername();
        User user = null;
        if (Validator.isEmail(username)){
            user = userService.getUserByEmail(username);
        }else {
            user = userService.getUserByUserName(username);
        }
        if (user == null){
            log.info("用户不存在!");
            return null;
        }
        //封装AuthenticationInfo,准备验证
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(
                user, user.getPassword(), ByteSource.Util.bytes(user.getSalt()), getName());
        return info;
    }

    /**
     * 重写方法,清除当前用户的的 授权缓存
     * @param principals
     */
    @Override
    public void clearCachedAuthorizationInfo(PrincipalCollection principals) {
        super.clearCachedAuthorizationInfo(principals);
    }

    /**
     * 重写方法,清除当前用户的 认证缓存
     * @param principals
     */
    @Override
    public void clearCachedAuthenticationInfo(PrincipalCollection principals) {
        super.clearCachedAuthenticationInfo(principals);
    }

    @Override
    public void clearCache(PrincipalCollection principals) {
        super.clearCache(principals);
    }

    /**
     * 自定义方法:清除所有 授权缓存
     */
    public void clearAllCachedAuthorizationInfo() {
        getAuthorizationCache().clear();
    }

    /**
     * 自定义方法:清除所有 认证缓存
     */
    public void clearAllCachedAuthenticationInfo() {
        getAuthenticationCache().clear();
    }

    /**
     * 自定义方法:清除所有的  认证缓存  和 授权缓存
     */
    public void clearAllCache() {
        clearAllCachedAuthenticationInfo();
        clearAllCachedAuthorizationInfo();
    }

    /**
     * 加密
     * @param args
     */
    public static void main(String[] args) {
        String hashAlgorithName = "MD5";
        String password = "123";
        int hashIterations = 1024;

        ByteSource credentialsSalt = ByteSource.Util.bytes("tom");
        Object obj = new SimpleHash(hashAlgorithName, password, credentialsSalt, hashIterations);
        log.info(obj);
    }
}

在resources下面新建config文件夹,并创建ehcache-shiro.xml文件




    
    

    
    

    
    
    

    
    
    


主要的配置文件都在这里了。
欢迎大家讨论,如果程序出现错误,轻喷~~

你可能感兴趣的:(SpringBoot,shiro)