package com.hollywood.common.security;
|
|
|
import com.alibaba.fastjson.JSONObject;
|
import com.hollywood.common.dto.RefAccessTokenDTO;
|
import com.hollywood.common.basic.Constant;
|
import com.hollywood.common.cache.CaffineCache;
|
import com.hollywood.common.exception.TokenException;
|
import com.hollywood.common.model.TUser;
|
import com.hollywood.common.swagger.GlobalResultEnum;
|
import com.hollywood.common.utils.JsonUtils;
|
import com.hollywood.common.utils.WebUtils;
|
import io.jsonwebtoken.Claims;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.security.authentication.AuthenticationManager;
|
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
|
import org.springframework.security.core.Authentication;
|
import org.springframework.security.core.context.SecurityContextHolder;
|
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
|
import org.springframework.util.Base64Utils;
|
import org.springframework.util.StringUtils;
|
|
import javax.servlet.http.HttpServletRequest;
|
import java.util.HashMap;
|
import java.util.Map;
|
import java.util.Objects;
|
import java.util.UUID;
|
|
/**
|
* Security相关操作
|
*
|
* @author xiaochen
|
* @date Jun 29, 2019
|
*/
|
@Slf4j
|
public class SecurityUtils {
|
/**
|
* 有效期,以秒为单位
|
*/
|
private static final long TOKEN_EXPIRE_TIME = 2 * 60 * 60 * 1000L;
|
|
/**
|
* 令牌存储
|
*/
|
private CaffineCache<String> accessTokenCache;
|
|
/**
|
* 刷新令牌存储
|
*/
|
private CaffineCache<String> refreshAccessTokenCache;
|
|
/**
|
* token过期时间
|
*/
|
private long tokenExpireTime;
|
|
|
/**
|
* token不需要托管
|
*/
|
public SecurityUtils() {
|
this(TOKEN_EXPIRE_TIME);
|
}
|
|
/**
|
* token不需要托管
|
*
|
* @param tokenExpireTime
|
*/
|
public SecurityUtils(long tokenExpireTime) {
|
this.tokenExpireTime = tokenExpireTime;
|
}
|
|
/**
|
* 数据构造,将token托管
|
*
|
* @param accessTokenCache
|
* @param refreshAccessTokenCache
|
*/
|
public SecurityUtils(CaffineCache<String> accessTokenCache, CaffineCache<String> refreshAccessTokenCache) {
|
this(TOKEN_EXPIRE_TIME, accessTokenCache, refreshAccessTokenCache);
|
}
|
|
/**
|
* 数据构造,将token托管
|
*
|
* @param tokenExpireTime
|
* @param accessTokenCache
|
* @param refreshAccessTokenCache
|
*/
|
public SecurityUtils(long tokenExpireTime, CaffineCache<String> accessTokenCache, CaffineCache<String> refreshAccessTokenCache) {
|
this.tokenExpireTime = tokenExpireTime;
|
this.accessTokenCache = accessTokenCache;
|
this.refreshAccessTokenCache = refreshAccessTokenCache;
|
}
|
|
/**
|
* 系统登录认证
|
*
|
* @param userName
|
* @param obj
|
* @param authenticationManager
|
* @param clazz
|
* @param <T>
|
* @return
|
*/
|
/*public <T> Map<String, Object> login(String userName, Object obj,
|
AuthenticationManager authenticationManager, Class<?> clazz) {
|
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userName, obj);
|
usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(WebUtils.request()));
|
// 执行登录认证过程
|
Authentication authentication = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
|
// 认证成功存储认证信息到上下文
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
// 生成令牌并返回给客户端
|
Map<String, String> tokenInfo = saveToken(userName);
|
Map<String, Object> map = new HashMap<>(2);
|
log.info("----------------------" + authentication.getDetails().toString());
|
map.put("userInfo", clazz.cast(authentication.getDetails()));
|
map.put("tokenInfo", tokenInfo);
|
return map;
|
}*/
|
|
|
|
/**
|
* 系统登录认证
|
*
|
* @param userName
|
* @param obj
|
* @param authenticationManager
|
* @param clazz
|
* @param flag true 管理后台 false 小程序
|
* @param <T>
|
* @return
|
*/
|
public <T> Map<String, Object> login(String userName, Object obj,
|
AuthenticationManager authenticationManager, Class<?> clazz, Integer flag) {
|
UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userName, obj);
|
Authentication authentication;
|
if (flag == 1) {
|
log.info("<<<<<<<<管理后台login开始<<<<<<<<");
|
// usernamePasswordAuthenticationToken.setDetails(obj);
|
// usernamePasswordAuthenticationToken.setAuthenticated(false);
|
// authentication = usernamePasswordAuthenticationToken;
|
|
|
usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userName, obj);
|
usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(WebUtils.request()));
|
// 执行登录认证过程
|
authentication = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
|
// 手机号存储
|
// usernamePasswordAuthenticationToken.setDetails(userName);
|
// usernamePasswordAuthenticationToken.setAuthenticated(false);
|
// authentication = usernamePasswordAuthenticationToken;
|
} else if (flag == 3) {
|
log.info("<<<<<<<<微信小程序login开始<<<<<<<<");
|
usernamePasswordAuthenticationToken.setDetails(obj);
|
usernamePasswordAuthenticationToken.setAuthenticated(false);
|
authentication = usernamePasswordAuthenticationToken;
|
} else {
|
usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(userName, obj);
|
usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(WebUtils.request()));
|
// 执行登录认证过程
|
authentication = authenticationManager.authenticate(usernamePasswordAuthenticationToken);
|
|
// usernamePasswordAuthenticationToken.setDetails(obj);
|
// usernamePasswordAuthenticationToken.setAuthenticated(false);
|
// authentication = usernamePasswordAuthenticationToken;
|
}
|
|
// 认证成功存储认证信息到上下文
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
// 生成令牌并返回给客户端
|
Map<String, String> tokenInfo = saveToken(JSONObject.parseObject(JsonUtils.toJsonString(authentication.getDetails())).getString("id"));
|
Map<String, Object> map = new HashMap<>(2);
|
map.put("userInfo", clazz.cast(authentication.getDetails()));
|
map.put("tokenInfo", tokenInfo);
|
log.info("用户返回数据:{}", JsonUtils.toJsonString(authentication.getDetails()));
|
return map;
|
}
|
|
/**
|
* 保存token
|
*
|
* @param userName
|
* @return
|
*/
|
public Map<String, String> saveToken(String userName) {
|
String token = JwtTokenUtils.generateToken(userName, this.tokenExpireTime);
|
// 返回token信息
|
String accessToken = new String(Base64Utils.encode(token.getBytes()));
|
String refreshToken = UUID.randomUUID().toString();
|
Map<String, String> tokenInfo = new HashMap<>(2);
|
// 将token托管
|
if (Objects.nonNull(this.accessTokenCache) && Objects.nonNull(this.refreshAccessTokenCache)) {
|
this.accessTokenCache.setKey(Constant.JWT_TOKEN_KEY_PREFIX + userName, accessToken);
|
this.refreshAccessTokenCache.setKey(Constant.JWT_REFRESH_TOKEN_KEY_PREFIX + userName, refreshToken);
|
}
|
tokenInfo.put("accessToken", accessToken);
|
tokenInfo.put("refreshToken", refreshToken);
|
return tokenInfo;
|
}
|
|
/**
|
* 通过刷新令牌 获取新的 访问令牌
|
*
|
* @param dto
|
* @return
|
*/
|
public String getAccessTokenByRefreshToken(RefAccessTokenDTO dto) {
|
// 判断 refreshToken 是否过期,若也已过期则需重新登录
|
String userName = dto.getAccount();
|
CaffineCache<String> tokenCache;
|
if (Objects.nonNull(this.refreshAccessTokenCache)) {
|
tokenCache = this.refreshAccessTokenCache;
|
} else {
|
throw new RuntimeException("未定义刷新令牌的存储");
|
}
|
String refreshToken = tokenCache.getKey(Constant.JWT_REFRESH_TOKEN_KEY_PREFIX + userName);
|
if (!StringUtils.hasLength(refreshToken)) {
|
throw new TokenException(GlobalResultEnum.LOGIN_EXPIRE);
|
}
|
if (!dto.getRefAccessToken().equals(refreshToken)) {
|
throw new TokenException(GlobalResultEnum.LOGIN_EXPIRE);
|
}
|
// 重新产生新的访问令牌
|
String accessToken = generateAccessTokenByRefreshToken(userName);
|
return accessToken;
|
}
|
|
|
/**
|
* 获取令牌进行检查并认证
|
*
|
* @param request
|
*/
|
public void checkAuthentication(HttpServletRequest request) {
|
String token = JwtTokenUtils.getToken(request);
|
// 获取摘要信息
|
Claims claims = JwtTokenUtils.getClaimsFromToken(token);
|
CaffineCache<String> tokenCache = null;
|
// 清理托管缓存
|
if (Objects.nonNull(this.accessTokenCache)) {
|
tokenCache = this.accessTokenCache;
|
}
|
// 如果token被托管
|
if (Objects.nonNull(tokenCache)) {
|
String accessToken = tokenCache.getKey(Constant.JWT_TOKEN_KEY_PREFIX + claims.getSubject());
|
if (Objects.isNull(accessToken)) {
|
throw new TokenException(GlobalResultEnum.TOKEN_EXPIRE.getCode(), GlobalResultEnum.TOKEN_EXPIRE.getMessage());
|
}
|
accessToken = new String(Base64Utils.decode(accessToken.getBytes()));
|
if (!token.equals(accessToken)) {
|
throw new TokenException(GlobalResultEnum.TOKEN_EXPIRE.getCode(), GlobalResultEnum.TOKEN_EXPIRE.getMessage());
|
}
|
}
|
// 获取令牌并根据令牌获取登录认证信息
|
Authentication authentication = JwtTokenUtils.getAuthenticatione(token);
|
// 设置登录认证信息到上下文
|
SecurityContextHolder.getContext().setAuthentication(authentication);
|
}
|
|
|
/**
|
* 设置JWT令牌过期
|
* 默认不支持手动过期,需要借助缓存托管
|
*
|
* @param
|
*/
|
public boolean invalidateToken(HttpServletRequest request) {
|
String accessToken = JwtTokenUtils.getToken(request);
|
String userName = JwtTokenUtils.getUsername(accessToken);
|
// 从缓存中清除JWT
|
// 清理托管缓存
|
if (Objects.nonNull(this.accessTokenCache)) {
|
return this.accessTokenCache.delKey(Constant.JWT_TOKEN_KEY_PREFIX + userName);
|
}
|
else {
|
generateAccessTokenByRefreshToken(userName);
|
// throw new RuntimeException("退出失败,此模式下无法手动过期");
|
return true;
|
}
|
}
|
|
/**
|
* 根据刷新令牌重新生成令牌
|
*
|
* @param userName 用户
|
* @return
|
*/
|
public String generateAccessTokenByRefreshToken(String userName) {
|
String token = JwtTokenUtils.generateToken(userName, this.tokenExpireTime);
|
String accessToken = new String(Base64Utils.encode(token.getBytes()));
|
CaffineCache<String> tokenCache = null;
|
if (Objects.nonNull(this.refreshAccessTokenCache)) {
|
tokenCache = this.refreshAccessTokenCache;
|
}
|
if (Objects.nonNull(tokenCache)) {
|
tokenCache.setKey(Constant.JWT_TOKEN_KEY_PREFIX + userName, accessToken);
|
}
|
return accessToken;
|
}
|
}
|