springboot整合shiro、ehcache

只需要一个自定义realm、一个shiro配置类和ehcache

自定义realm:

package com.example.demo.config;

import com.example.demo.entity.RoleEntity;
import com.example.demo.entity.UserEntity;
import com.example.demo.jpa.UserJpa;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.HostUnauthorizedException;
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.HashSet;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author FastKing
 * @version 1.0
 * @date 2018/9/19 11:54
 **/
public class MyShiroRealm extends AuthorizingRealm {

	@Autowired
	private UserJpa userJpa;

	/**
	 * 权限认证
	 *
	 * @param principalCollection
	 * @author FastKing
	 * @date 10:11 2018/9/27
	 **/
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) throws AuthorizationException {
		SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
		UserEntity userEntity = (UserEntity) principalCollection.getPrimaryPrincipal();
		Set<RoleEntity> roleEntitySet = userEntity.getRoleEntitySet();
		Set<String> permissionNameSet = new HashSet<>();
		simpleAuthorizationInfo.setRoles(roleEntitySet.stream().map(RoleEntity::getName).collect(Collectors.toSet()));
		roleEntitySet.forEach(roleEntity -> roleEntity.getPermissionEntitySet().forEach(permissionEntity -> permissionNameSet.add(permissionEntity.getName())));
		simpleAuthorizationInfo.setStringPermissions(permissionNameSet);
		if (permissionNameSet.size() <= 0) {
			throw new HostUnauthorizedException("没有权限");
		}
		return simpleAuthorizationInfo;
	}

	/**
	 * 身份认证
	 *
	 * @param authenticationToken
	 * @author FastKing
	 * @date 16:48 2018/9/27
	 **/
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
		UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
		String username = usernamePasswordToken.getUsername();
		UserEntity userEntity = userJpa.findByUsername(username);
		if (null == userEntity) {
			throw new UnknownAccountException("帐号不存在");
		}
		String password = String.valueOf(usernamePasswordToken.getPassword());
		SimpleHash md5 = new SimpleHash("MD5", password, ByteSource.Util.bytes(userEntity.getSalt()), 1024);
		if (!StringUtils.equals(userEntity.getPassword(), md5.toString())) {
			throw new IncorrectCredentialsException("密码错误");
		}
		if (userEntity.getIsLocked() == 1) {
			throw new LockedAccountException("帐号已锁定");
		}
		if (userEntity.getIsForbidden() == 1) {
			throw new DisabledAccountException("帐号已禁用");
		}
		return new SimpleAuthenticationInfo(username, md5, ByteSource.Util.bytes(userEntity.getSalt()), getName());
	}
}

自定义realm不难理解,主要是对账号进行认证和授权

认证方法中的密码使用了MD5和盐值加密

授权方法的作用是把权限写入cookie,一般使用jsp中的shiro标签库时会重写授权方法

下面是shiro配置类:

package com.example.demo.config;

import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler;
import org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO;
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.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.LinkedList;

/**
 * @author FastKing
 * @version 1.0
 * @date 2018/9/19 11:02
 **/
@Configuration
public class ShiroConfig {

	/**
	 * @param securityManager
	 * @author FastKing
	 * @date 17:00 2018/9/27
	 **/
	@Bean
	public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		//配置安全管理器,shiro核心,负责与其他安全组件的交互,并管理Subject
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		//配置登录页面
		shiroFilterFactoryBean.setLoginUrl("/view/login.html");
		//配置登录成功页面
		shiroFilterFactoryBean.setSuccessUrl("/view/user/index.html");
		//配置未授权页面
		shiroFilterFactoryBean.setUnauthorizedUrl("/view/error/403.html");

		//配置拦截器
		LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
		filterChainDefinitionMap.put("/view/**", "user");
		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
		return shiroFilterFactoryBean;
	}

	/**
	 * 加密器
	 *
	 * @author FastKing
	 * @date 16:59 2018/9/27
	 **/
	@Bean
	public HashedCredentialsMatcher hashedCredentialsMatcher() {
		HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
		//散列算法
		hashedCredentialsMatcher.setHashAlgorithmName("MD5");
		//散列次数
		hashedCredentialsMatcher.setHashIterations(1024);
		//是否存储散列后的密码为16进制
		hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true);
		return hashedCredentialsMatcher;
	}

	/**
	 * 安全管理器
	 *
	 * @author FastKing
	 * @date 17:23 2018/9/27
	 **/
	@Bean
	public SecurityManager securityManager() {
		DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
		defaultWebSecurityManager.setRealm(myShiroRealm());
		///设置session管理器
		defaultWebSecurityManager.setSessionManager(getDefaultWebSessionManager());
		//设置记住我管理器
		defaultWebSecurityManager.setRememberMeManager(cookieRememberMeManager());
		//设置缓存管理器
		defaultWebSecurityManager.setCacheManager(ehCacheManager());
		return defaultWebSecurityManager;
	}

	/**
	 * 自定义realm
	 *
	 * @author FastKing
	 * @date 12:38 2018/9/28
	 **/
	@Bean
	public MyShiroRealm myShiroRealm() {
		MyShiroRealm myShiroRealm = new MyShiroRealm();
		myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
		return myShiroRealm;
	}

	/**
	 * session管理器
	 *
	 * @author FastKing
	 * @date 13:06 2018/9/28
	 **/
	private DefaultWebSessionManager getDefaultWebSessionManager() {
		DefaultWebSessionManager defaultWebSessionManager = new DefaultWebSessionManager();
		//设置过期时间30分钟
		defaultWebSessionManager.setGlobalSessionTimeout(1800000);
		//session定期验证
		defaultWebSessionManager.setSessionValidationScheduler(getExecutorServiceSessionValidationScheduler());
		defaultWebSessionManager.setDeleteInvalidSessions(true);
		//session cookie
		defaultWebSessionManager.setSessionIdCookie(getSessionIdCookie());
		defaultWebSessionManager.setSessionIdCookieEnabled(true);
		defaultWebSessionManager.setSessionValidationSchedulerEnabled(true);
		//session监听
		LinkedList<SessionListener> list = new LinkedList<>();
		list.add(new MyShiroSessionListener());
		defaultWebSessionManager.setSessionListeners(list);
		//session的存储
		EnterpriseCacheSessionDAO cacheSessionDAO = new EnterpriseCacheSessionDAO();
		defaultWebSessionManager.setCacheManager(ehCacheManager());
		defaultWebSessionManager.setSessionDAO(cacheSessionDAO);

		return defaultWebSessionManager;
	}

	/**
	 * ehcache缓存管理器
	 *
	 * @author FastKing
	 * @date 12:39 2018/9/28
	 **/
	@Bean
	public EhCacheManager ehCacheManager() {
		EhCacheManager ehCacheManager = new EhCacheManager();
		ehCacheManager.setCacheManagerConfigFile("classpath:shiro-ehcache.xml");
		return ehCacheManager;
	}

	/**
	 * rememberMe cookie对象
	 *
	 * @author FastKing
	 * @date 12:49 2018/9/28
	 **/
	private SimpleCookie rememberMeCookie() {
		SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
		//防止cookie暴露给客户端
		simpleCookie.setHttpOnly(true);
		//设置过期时间30天
		simpleCookie.setMaxAge(2592000);
		return simpleCookie;
	}

	private SimpleCookie getSessionIdCookie() {
		SimpleCookie simpleCookie = new SimpleCookie("sid");
		simpleCookie.setHttpOnly(true);
		simpleCookie.setMaxAge(-1);
		return simpleCookie;
	}

	/**
	 * 记住我管理器
	 *
	 * @author FastKing
	 * @date 12:52 2018/9/28
	 **/
	private CookieRememberMeManager cookieRememberMeManager() {
		CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
		cookieRememberMeManager.setCookie(rememberMeCookie());
		cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
		return cookieRememberMeManager;
	}

	/**
	 * session验证器
	 *
	 * @author FastKing
	 * @date 13:18 2018/9/28
	 **/
	private ExecutorServiceSessionValidationScheduler getExecutorServiceSessionValidationScheduler() {
		ExecutorServiceSessionValidationScheduler executorServiceSessionValidationScheduler = new ExecutorServiceSessionValidationScheduler();
		//15分钟校验一次
		executorServiceSessionValidationScheduler.setInterval(900000);
		return executorServiceSessionValidationScheduler;

	}

}

配置类中都写了注释,也很容易理解

最后时ehcache.xml,如果你不是用ehcache管理缓存,可以忽略:

<?xml version="1.0" encoding="UTF-8"?>
<ehcache updateCheck="false" dynamicConfig="false">
    <diskStore path="java.io.tmpdir"/>

    <cache name="users"
           timeToLiveSeconds="300"
           maxEntriesLocalHeap="1000"/>

    <!--
        name:缓存名称。
        maxElementsInMemory:缓存最大个数。
        eternal:对象是否永久有效,一但设置了,timeout将不起作用。
        timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
        timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
        overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。
        diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
        maxElementsOnDisk:硬盘最大缓存个数。
        diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
        diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
        memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
        clearOnFlush:内存数量最大时是否清除。
    -->
    <defaultCache name="defaultCache"
                  maxElementsInMemory="10000"
                  eternal="false"
                  timeToIdleSeconds="120"
                  timeToLiveSeconds="120"
                  overflowToDisk="false"
                  maxElementsOnDisk="100000"
                  diskPersistent="false"
                  diskExpiryThreadIntervalSeconds="120"
                  memoryStoreEvictionPolicy="LRU"/>
</ehcache>

写个controller测试一下:

package com.example.demo.controller;

import com.example.demo.jpa.UserJpa;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * @ClassName HelloController
 * @Description TODO
 * @Author FastKing
 * @Date 2018/9/13 9:02
 * @Version 1.0
 **/
@RestController
public class HelloController {

	@Autowired
	private UserJpa userJpa;

	@ResponseBody
	@RequestMapping(value = "/login/{username}/{password}/{rememberMe}", method = RequestMethod.GET)
	public Object login(@PathVariable String username, @PathVariable String password, @PathVariable String rememberMe) {
		try {
			SecurityUtils.getSubject().login(new UsernamePasswordToken(username, password, Boolean.parseBoolean(rememberMe)));
		} catch (Exception e) {
			return e.getMessage();
		}
		return null;
	}

	@RequestMapping(value = "/list")
	public Object list() {
		return userJpa.findAll();
	}
}

如果你要使用“记住我”功能,把参数传到UsernamePasswordToken就可以

猜你喜欢

转载自blog.csdn.net/weixin_39841589/article/details/83098331