package com.iplatform.security; import com.iplatform.base.DefaultUserPrincipal; import com.iplatform.base.SecurityConstants; import com.iplatform.base.VariableConstants; import com.iplatform.base.util.TokenUtils; import com.iplatform.core.TokenAwareContext; import com.iplatform.core.TokenEntity; import com.iplatform.model.po.S_user_core; import com.iplatform.security.config.SecurityProperties; import com.walker.infrastructure.ApplicationRuntimeException; import com.walker.web.Constants; import com.walker.web.ResponseCode; import com.walker.web.ResponseValue; import com.walker.web.TokenException; import com.walker.web.TokenGenerator; import com.walker.web.UserOnlineProvider; import com.walker.web.UserPrincipal; import com.walker.web.util.ServletUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.web.filter.OncePerRequestFilter; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { protected final transient Logger logger = LoggerFactory.getLogger(getClass()); private TokenGenerator tokenGenerator; private UserOnlineProvider userOnlineProvider; private DefaultUserDetailsService defaultUserDetailsService; // 2023-03-28 private SecurityProperties securityProperties; public void setSecurityProperties(SecurityProperties securityProperties) { this.securityProperties = securityProperties; } public void setDefaultUserDetailsService(DefaultUserDetailsService defaultUserDetailsService) { this.defaultUserDetailsService = defaultUserDetailsService; } public void setUserOnlineProvider(UserOnlineProvider userOnlineProvider) { this.userOnlineProvider = userOnlineProvider; } public void setTokenGenerator(TokenGenerator tokenGenerator) { this.tokenGenerator = tokenGenerator; } @Override protected void doFilterInternal(HttpServletRequest request , HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String token = TokenUtils.getAuthorizationToken(request); if(token != null){ try{ String data = tokenGenerator.validateToken(token, VariableConstants.TOKEN_SECRET); logger.debug("token_data = " + data); String[] userIdAndKey = TokenUtils.getUserIdAndKey(data); if(userIdAndKey == null || userIdAndKey.length != 3){ throw new ApplicationRuntimeException("token携带用户信息解析错误:" + data); } String uuid = userIdAndKey[2]; String loginId = userIdAndKey[1]; DefaultUserDetails userDetails = null; // 根据token获取用户对象 DefaultUserPrincipal userPrincipal = this.acquireAuthenticationUser(uuid); if(userPrincipal == null){ // 2022-11-15,这里要告诉客户端刷新token // redis中缓存登录时间应小于token失效时间 userDetails = this.defaultUserDetailsService.acquireUserPrincipal(loginId); // 2023-09-03 当前端传入的 token 是无效的情况下,如:用户已不存在,需要删除登录缓存 if(userDetails == null){ this.userOnlineProvider.removeUserPrincipal(uuid); this.logger.warn("用户已不存在,删除登录状态缓存:{}", uuid); return; } userPrincipal = (DefaultUserPrincipal)userDetails.getUserPrincipal(); logger.debug("token需要刷新: " + userPrincipal.getUserName()); this.tellClientRefreshToken(response, uuid, userPrincipal); // 2022-11-15,以下响应代码不再需要,因为已经刷新token,重新缓存登录用户, // ServletUtils.renderString(response, ResponseValue.error(ResponseCode.RE_LOGIN.getCode(), "token不存在或已过期,请重新认证")); // return; } else { userDetails = this.acquireUserDetails(userPrincipal); } UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authentication); // 2023-08-05 TokenAwareContext.setCurrentToken(new TokenEntity(uuid, loginId)); } catch (TokenException ex){ if(ex.isExpired()){ ServletUtils.renderString(response, ResponseValue.error(ResponseCode.RE_LOGIN.getCode(), "token已过期,请重新获取")); return; } System.out.println(ex.getTitle()); ServletUtils.renderString(response, ResponseValue.error(ResponseCode.ERROR.getCode(), ex.getTitle())); return; } catch (Exception ex){ logger.error("根据token获得登录信息错误:" + ex.getMessage(), ex); ServletUtils.renderString(response, ResponseValue.error(ResponseCode.ERROR.getCode(), ex.getMessage())); return; } } // 要尝试:token不存在不能访问 // logger.warn("token不存在,无法访问系统更能"); filterChain.doFilter(request, response); // 2023-08-05 TokenAwareContext.clearCurrentToken(); } /** * 通知终端刷新token,token会写入响应头中: TokenRefresh 字段。 * @param response * @param uuid * @param userPrincipal */ private void tellClientRefreshToken(HttpServletResponse response, String uuid, DefaultUserPrincipal userPrincipal){ // String token = this.tokenGenerator.createToken(uuid, userPrincipal.getId() // , VariableConstants.DEFAULT_TOKEN_EXPIRED_MINUTES, VariableConstants.TOKEN_SECRET); // 刷新token,通常PC端会用到,移动端时间长使用几率低,因此这里找PC端配置。2023-03-28 String token = TokenUtils.generateToken(userPrincipal.getId() , userPrincipal.getUserName(), uuid, this.tokenGenerator, securityProperties.getTokenExpireWeb()); // 重新设置用户创建token时间 userPrincipal.setLastLoginTime(System.currentTimeMillis()); this.userOnlineProvider.cacheUserPrincipal(uuid, userPrincipal, securityProperties.getTokenExpireWeb()); response.addHeader(Constants.TOKEN_HEADER_REFRESH, token); if(this.logger.isDebugEnabled()){ logger.debug("刷新token, uuid = " + uuid + ", " + token); } } private DefaultUserDetails acquireUserDetails(UserPrincipal userPrincipal){ DefaultUserDetails userDetails = new DefaultUserDetails(userPrincipal); // if(userPrincipal.getUserInfo().isSupervisor()){ if(userDetails.isSupervisor()){ userDetails.addGrantedAuthority(SecurityConstants.ROLE_SUPER_ADMIN); userDetails.addGrantedAuthority(SecurityConstants.ROLE_ADMIN); userDetails.addGrantedAuthority(SecurityConstants.ROLE_USER); } else { // 当前数据库只有一个普通角色:2 // userDetails.addGrantedAuthority("2"); // 在用户登录后,缓存的就有角色ID集合,这里每次访问重新设置到对象中。2022-11-11 List roleIdList = userPrincipal.getRoleIdList(); logger.info("缓存中获取 userPrincipal.getRoleIdList() = " + roleIdList); if(roleIdList != null){ for(String roleId : roleIdList){ userDetails.addGrantedAuthority(roleId); } } } return userDetails; } /** * 模拟测试,后续修改为数据库方式。

* 从缓存中读取用户登录信息,如果换成失效为空。 * @param uuid * @return * @date 2022-11-15 */ private DefaultUserPrincipal acquireAuthenticationUser(String uuid){ /*String userId = userIdAndKey[0]; if(userId.equals("0")){ // 0 为超级管理员 return MockPrincipalUtils.createSupervisor(); } else { // 其他值模拟为普通用户 return MockPrincipalUtils.createNormalUser("100"); }*/ // uuid // String loginKey = userIdAndKey[2]; return (DefaultUserPrincipal)this.userOnlineProvider.getUserPrincipal(uuid); } }