springboot+shiro+ehcache 迈不过友情╰ 2022-05-11 12:44 204阅读 0赞 项目中有使用shiro特地把它记录下来,免得自己又忘了: <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>${shiro-version}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>${shiro-version}</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-ehcache</artifactId> <version>${shiro-version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId>net.sf.ehcache</groupId> <artifactId>ehcache</artifactId> <version>2.4.8</version> </dependency> ehcahe的版本使用的2.5之前的版本,2.5之后的版本不允许使用同一个缓存空间; 自己定义的一个缓存对象,自定义两个方法存取: package com.koala.console.cache; import lombok.Data; import java.io.Serializable; /** * @author PC_gongyiyang * @Auther: gongyiyang * @Date: 2018/10/18 19:19 * @Description: */ @Data public class AuthenticationSession implements Serializable { private static final long serialVersionUID = 1L; /** * @Fields <font color="blue">cacheName</font> * @description 缓存名 */ private String cacheName; /** * @Fields <font color="blue">identify</font> * @description 唯一区别串 */ private String identify; /** * @Fields <font color="blue">data</font> * @description 缓存主体数据 */ private Object data; public AuthenticationSession() { } public AuthenticationSession(String cacheName, String identify) { this.cacheName = cacheName; this.identify = identify; } public AuthenticationSession(String cacheName, String identify, Object data) { this.cacheName = cacheName; this.identify = identify; this.data = data; } public <T> T get(Class<T> type) { return data == null ? null : (T) data; } } package com.koala.console.cache; import com.koala.console.constant.ModelConstant; import org.apache.commons.lang.StringUtils; import org.springframework.cache.Cache; import org.springframework.cache.ehcache.EhCacheCacheManager; import org.springframework.stereotype.Component; /** * @author PC_gongyiyang * @Auther: gongyiyang * @Date: 2018/10/18 18:07 * @Description: */ @Component public class EhcacheCacheManager extends EhCacheCacheManager { public void setSession(AuthenticationSession session) { this.getCache(session.getCacheName()).put(session.getIdentify().substring(ModelConstant.TOKEN_LENGTH), session); } public AuthenticationSession getSession(AuthenticationSession session) { if (StringUtils.isBlank(session.getCacheName())) { throw new RuntimeException("cache name is empty"); } String identify = session.getIdentify(); if (StringUtils.isBlank(identify) || identify.length() <= ModelConstant.TOKEN_LENGTH) { throw new RuntimeException("login token identify length lacks"); } Cache cache = this.getCache(session.getCacheName()); AuthenticationSession authenticationSession = cache.get(identify.substring(ModelConstant.TOKEN_LENGTH), AuthenticationSession.class); if (authenticationSession != null && identify.equals(authenticationSession.getIdentify())) { return authenticationSession; } return null; } } package com.koala.console.ac.oauth2; import com.koala.console.constant.StatusCode; import com.koala.console.exception.GlobalException; import lombok.extern.slf4j.Slf4j; import java.security.MessageDigest; import java.util.UUID; /** * @author <font color="red"><b>Gong.YiYang</b></font> * @Date 2018年8月27日 * @Version * @Description 生成token */ @Slf4j public class TokenGenerator { private TokenGenerator() { } static String uu; public static String generateValue() { try { uu = generateValue(UUID.randomUUID().toString()); } catch (Exception e) { log.error(e.getMessage()); } return uu; } private static final char[] HEX_CODE = "0123456789abcdef".toCharArray(); public static String toHexString(byte[] data) { if (data == null) { return null; } StringBuilder r = new StringBuilder(data.length * 2); for (byte b : data) { r.append(HEX_CODE[(b >> 4) & 0xF]); r.append(HEX_CODE[(b & 0xF)]); } return r.toString(); } public static String generateValue(String param) throws GlobalException { try { MessageDigest algorithm = MessageDigest.getInstance("MD5"); algorithm.reset(); algorithm.update(UUID.randomUUID().toString().getBytes()); byte[] messageDigest = algorithm.digest(); return toHexString(messageDigest) + param; } catch (Exception e) { throw new GlobalException(StatusCode.FAILED, "生成Token失败"); } } } package com.koala.console.ac.config; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.filter.DelegatingFilterProxy; /** * @author <font color="red"><b>Gong.YiYang</b></font> * @Date 2018年8月27日 * @Version * @Description Filter配置 */ @Configuration public class FilterConfig { @Bean public FilterRegistrationBean shiroFilterRegistration() { FilterRegistrationBean registration = new FilterRegistrationBean(); registration.setFilter(new DelegatingFilterProxy("shiroFilter")); //该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理 registration.addInitParameter("targetFilterLifecycle", "true"); registration.setEnabled(true); registration.setOrder(Integer.MAX_VALUE - 1); registration.addUrlPatterns("/*"); return registration; } } package com.koala.console.ac.config; import com.koala.console.ac.oauth2.Oauth2Filter; import com.koala.console.ac.oauth2.Oauth2Realm; import org.apache.shiro.cache.ehcache.EhCacheManager; import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.session.mgt.SessionManager; 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.session.mgt.DefaultWebSessionManager; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import javax.servlet.Filter; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; /** * @author <font color="red"><b>Gong.YiYang</b></font> * @Date 2018年8月27日 * @Version * @Description Shiro配置 */ @Configuration public class ShiroConfig { @Bean("oAuth2Realm") public Oauth2Realm oauth2Realm() { Oauth2Realm oauth2Realm = new Oauth2Realm(); oauth2Realm.setCacheManager(ehCacheManager()); oauth2Realm.setAuthorizationCacheName("authorization_cache"); oauth2Realm.setAuthenticationCacheName("authentication_cache"); return oauth2Realm; } @Bean("sessionManager") public SessionManager sessionManager() { DefaultWebSessionManager sessionManager = new DefaultWebSessionManager(); sessionManager.setSessionValidationSchedulerEnabled(true); sessionManager.setSessionIdCookieEnabled(false); return sessionManager; } @Bean("securityManager") public SecurityManager securityManager(Oauth2Realm oAuth2Realm, SessionManager sessionManager) { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(oAuth2Realm); securityManager.setCacheManager(ehCacheManager()); securityManager.setSessionManager(sessionManager); return securityManager; } @Bean("shiroFilter") public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean(); shiroFilter.setSecurityManager(securityManager); // oauth过滤 Map<String, Filter> filters = new HashMap<>(16); filters.put("oauth2", new Oauth2Filter()); shiroFilter.setFilters(filters); Map<String, String> filterMap = new LinkedHashMap<>(); filterMap.put("/api/v1/ac/user/token/login", "anon"); filterMap.put("/api/v1/classificationPicture/findClassificationPictureById", "anon"); filterMap.put("/api/v1/file/**", "anon"); filterMap.put("/api/**", "oauth2"); filterMap.put("/**", "anon"); shiroFilter.setFilterChainDefinitionMap(filterMap); return shiroFilter; } @Bean("lifecycleBeanPostProcessor") public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() { return new LifecycleBeanPostProcessor(); } @Bean public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator(); proxyCreator.setProxyTargetClass(true); return proxyCreator; } @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); advisor.setSecurityManager(securityManager); return advisor; } @Bean public EhCacheManager ehCacheManager() { EhCacheManager manager = new EhCacheManager(); manager.setCacheManagerConfigFile("classpath:ehcache.xml"); return manager; } } package com.koala.console.ac.oauth2; import com.alibaba.fastjson.JSON; import com.koala.console.utils.Result; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; import org.apache.http.HttpStatus; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.web.filter.authc.AuthenticatingFilter; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @author <font color="red"><b>Gong.YiYang</b></font> * @Date 2018年8月27日 * @Version * @Description oauth2过滤器 自定义token替换shiro的token实现 */ @Slf4j public class Oauth2Filter extends AuthenticatingFilter { @Override protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception { //获取请求token String token = getRequestToken((HttpServletRequest) request); if(StringUtils.isBlank(token)){ return null; } return new Oauth2Token(token); } @Override protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { return false; } /** * 检查token */ @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { //获取请求token,如果token不存在,直接返回401 String token = getRequestToken((HttpServletRequest) request); if(StringUtils.isBlank(token)){ HttpServletResponse httpResponse = (HttpServletResponse) response; Result<Void> result = new Result<>(); result.setStatus(HttpStatus.SC_UNAUTHORIZED); result.setMessage("Token can not be empty"); String json = JSON.toJSONString(result); httpResponse.getWriter().print(json); return false; } return executeLogin(request, response); } /** * 登录失败 */ @Override protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) { HttpServletResponse httpResponse = (HttpServletResponse) response; httpResponse.setContentType("application/json;charset=utf-8"); try { Throwable throwable = e.getCause() == null ? e : e.getCause(); Result<Void> result = new Result<>(); result.setStatus(HttpStatus.SC_UNAUTHORIZED); result.setMessage(throwable.getMessage()); String json = JSON.toJSONString(result); httpResponse.getWriter().print(json); } catch (Exception ee) { log.debug("登录失败"); } return false; } /** * 获取请求的token */ private String getRequestToken(HttpServletRequest httpRequest){ //从header中获取token String token = httpRequest.getHeader("token"); //如果header中不存在token,则从参数中获取token if(StringUtils.isBlank(token)){ token = httpRequest.getParameter("token"); } return token; } } package com.koala.console.ac.oauth2; import com.koala.console.ac.model.SysUser; import com.koala.console.ac.service.SysUserService; import com.koala.console.cache.AuthenticationSession; import com.koala.console.cache.EhcacheCacheManager; import com.koala.console.constant.ModelConstant; import com.koala.console.utils.ShiroUtils; 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.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Lazy; import javax.annotation.Resource; import java.util.Set; /** * @author <font color="red"><b>Gong.YiYang</b></font> * @Date 2018年8月27日 * @Version * @Description 认证 */ public class Oauth2Realm extends AuthorizingRealm { @Resource private EhcacheCacheManager cacheManager; @Autowired @Lazy private SysUserService sysUserService; @Override public boolean supports(AuthenticationToken token) { return token instanceof Oauth2Token; } /** * 授权(验证权限时调用) */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { Long userId = ShiroUtils.getUserId(); //用户角色 Set<String> rolesSet = sysUserService.listUserRoles(userId); //用户权限 Set<String> permsSet = sysUserService.listUserPerms(userId); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.setRoles(rolesSet); info.setStringPermissions(permsSet); return info; } /** * 认证(登录时调用) */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) { String accessToken = (String) token.getPrincipal(); AuthenticationSession session = cacheManager.getSession(new AuthenticationSession(ModelConstant.CLOUD_USER, accessToken)); //token失效 if (session == null) { throw new IncorrectCredentialsException("token失效,请重新登录"); } SysUser sysUser = session.get(SysUser.class); //查询用户信息 SysUser user = sysUserService.getUserById(sysUser.getId()); //账号锁定 if (null == user || user.getStatus() == 0) { throw new LockedAccountException("账号已被锁定,请联系管理员"); } return new SimpleAuthenticationInfo(user, accessToken, getName()); } } package com.koala.console.ac.oauth2; import org.apache.shiro.authc.AuthenticationToken; /** * @author <font color="red"><b>Gong.YiYang</b></font> * @Date 2018年8月27日 * @Version * @Description token */ public class Oauth2Token implements AuthenticationToken { private static final long serialVersionUID = 1L; private String token; public Oauth2Token(String token){ this.token = token; } @Override public String getPrincipal() { return token; } @Override public Object getCredentials() { return getPrincipal(); } } public Result<SysUserToken> login(@RequestBody SysUser sysUser) { Result<SysUserToken> res = new Result<>(); try { SysUser user = sysUserService.getByUserName(sysUser.getUsername()); if (user == null || !sysUser.getPassword().equals(user.getPassword())) { res.setStatus(StatusCode.FAILED); res.setMessage("用户名或密码错误"); return res; } else if (user.getStatus() == 0) { res.setStatus(StatusCode.ACCOUNT_LOCKOUT); res.setMessage("账号已被锁定,请联系管理员"); return res; } SysUserToken sysUserToken = sysUserService.saveUserToken(user); List<String> rolelist = sysUserService.getRoleByUserId(user.getId()); sysUserToken.setRoleName(rolelist); res.setData(sysUserToken); res.setStatus(StatusCode.SUCCESSFUL); res.setMessage("登录成功"); } catch (Exception e) { res.setStatus(StatusCode.FAILED); res.setMessage(e.getMessage()); } return res; } <?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" updateCheck="false"> <diskStore path="java.io.tmpdir/Tmp_EhCache"/> <defaultCache eternal="false" maxElementsInMemory="10000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="1800" timeToLiveSeconds="259200" memoryStoreEvictionPolicy="LRU"/> <cache name="cloud_user" eternal="false" maxElementsInMemory="5000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="1800" timeToLiveSeconds="1800" memoryStoreEvictionPolicy="LRU"/> <cache name="authentication_cache" eternal="false" maxElementsInMemory="5000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="1800" timeToLiveSeconds="1800" memoryStoreEvictionPolicy="LRU"/> <cache name="authorization_cache" eternal="false" maxElementsInMemory="5000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="1800" timeToLiveSeconds="1800" memoryStoreEvictionPolicy="LRU"/> </ehcache>
还没有评论,来说两句吧...