项目中有使用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>