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<S_user_core> 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<String> roleIdList = userPrincipal.getRoleIdList();
|
logger.info("缓存中获取 userPrincipal.getRoleIdList() = " + roleIdList);
|
if(roleIdList != null){
|
for(String roleId : roleIdList){
|
userDetails.addGrantedAuthority(roleId);
|
}
|
}
|
}
|
return userDetails;
|
}
|
|
/**
|
* 模拟测试,后续修改为数据库方式。<p></p>
|
* 从缓存中读取用户登录信息,如果换成失效为空。
|
* @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);
|
}
|
}
|