package com.jilongda.common.security; import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.jilongda.common.dto.RefAccessTokenDTO; import com.jilongda.common.basic.Constant; import com.jilongda.common.cache.CaffineCache; import com.jilongda.common.exception.TokenException; import com.jilongda.common.model.TUser; import com.jilongda.common.swagger.GlobalResultEnum; import com.jilongda.common.utils.JsonUtils; import com.jilongda.common.utils.WebUtils; import io.jsonwebtoken.Claims; import lombok.extern.slf4j.Slf4j; 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 accessTokenCache; /** * 刷新令牌存储 */ private CaffineCache 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 accessTokenCache, CaffineCache refreshAccessTokenCache) { this(TOKEN_EXPIRE_TIME, accessTokenCache, refreshAccessTokenCache); } /** * 数据构造,将token托管 * * @param tokenExpireTime * @param accessTokenCache * @param refreshAccessTokenCache */ public SecurityUtils(long tokenExpireTime, CaffineCache accessTokenCache, CaffineCache refreshAccessTokenCache) { this.tokenExpireTime = tokenExpireTime; this.accessTokenCache = accessTokenCache; this.refreshAccessTokenCache = refreshAccessTokenCache; } /** * 系统登录认证 * * @param userName * @param obj * @param authenticationManager * @param clazz * @param * @return */ /*public Map 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 tokenInfo = saveToken(userName); Map 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 * @return */ public Map 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 == 2) { //短信登录 usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(WebUtils.request())); usernamePasswordAuthenticationToken.setAuthenticated(false); authentication = usernamePasswordAuthenticationToken; } else if (flag == 3) { log.info("<<<<<<<<微信小程序login开始<<<<<<<<"); usernamePasswordAuthenticationToken.setDetails(obj); usernamePasswordAuthenticationToken.setAuthenticated(false); authentication = usernamePasswordAuthenticationToken; } else { usernamePasswordAuthenticationToken.setDetails(obj); usernamePasswordAuthenticationToken.setAuthenticated(false); authentication = usernamePasswordAuthenticationToken; } // 认证成功存储认证信息到上下文 SecurityContextHolder.getContext().setAuthentication(authentication); // 生成令牌并返回给客户端 Map tokenInfo = saveToken(userName); Map map = new HashMap<>(2); if(flag!=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 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 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 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 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); // Object principal = authentication.getPrincipal(); // if (principal instanceof SecurityUserDetails) { // SecurityUserDetails userDetails = (SecurityUserDetails) principal; // if (userDetails.isFrozen()) { // 假设isFrozen()方法用于检查用户是否被冻结 // throw new TokenException(GlobalResultEnum.ACCOUNT_FROZEN.getCode(), GlobalResultEnum.ACCOUNT_FROZEN.getMessage()); // } // } else { // throw new IllegalStateException("Principal is not of type SecurityUserDetails"); // } } /** * 设置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 { throw new RuntimeException("退出失败,此模式下无法手动过期"); } } /** * 根据刷新令牌重新生成令牌 * * @param userName 用户 * @return */ public String generateAccessTokenByRefreshToken(String userName) { String token = JwtTokenUtils.generateToken(userName, this.tokenExpireTime); String accessToken = new String(Base64Utils.encode(token.getBytes())); CaffineCache 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; } }