Skip to content

Commit

Permalink
Merge pull request #6899 from EightMonth/springboot3_sas
Browse files Browse the repository at this point in the history
缩短token长度,适配主分支前端页面登录
  • Loading branch information
zhangdaiscott authored Aug 21, 2024
2 parents f4712ba + 935e118 commit f04f7f9
Show file tree
Hide file tree
Showing 16 changed files with 8,507 additions and 10,888 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,7 @@ public static boolean verify(String token, String username, String secret) {
public static String getUsername(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
LoginUser loginUser = JSONObject.parseObject(jwt.getClaim("sub").asString(), LoginUser.class);
return loginUser.getUsername();
return jwt.getClaim("username").asString();
} catch (JWTDecodeException e) {
return null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.jeecg.common.util;

import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.jeecg.common.api.CommonAPI;
Expand All @@ -11,14 +12,6 @@
import org.jeecg.common.system.util.JwtUtil;
import org.jeecg.common.system.vo.LoginUser;

import jakarta.servlet.http.HttpServletRequest;
import org.jeecg.config.security.JeecgRedisOAuth2AuthorizationService;
import org.springframework.data.redis.serializer.SerializationException;
import org.springframework.security.oauth2.server.authorization.OAuth2Authorization;
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;

import java.util.Objects;

/**
* @Author scott
* @Date 2019/9/23 14:12
Expand Down Expand Up @@ -122,7 +115,7 @@ public static boolean verifyToken(String token, CommonAPI commonApi, RedisUtil r
throw new JeecgBoot401Exception("账号已被锁定,请联系管理员!");
}
// 校验token是否超时失效 & 或者账号密码是否错误
if (!jwtTokenRefresh(token, username, user.getPassword())) {
if (!jwtTokenRefresh(token, username, user.getPassword(), redisUtil)) {
throw new JeecgBoot401Exception(CommonConstant.TOKEN_IS_INVALID_MSG);
}
return true;
Expand Down Expand Up @@ -151,15 +144,6 @@ private static boolean jwtTokenRefresh(String token, String userName, String pas
return false;
}

private static boolean jwtTokenRefresh(String token, String userName, String passWord) {
JeecgRedisOAuth2AuthorizationService authRedis = SpringContextUtils.getBean(JeecgRedisOAuth2AuthorizationService.class);
OAuth2Authorization authorization = authRedis.findByToken(token, OAuth2TokenType.ACCESS_TOKEN);
if (Objects.nonNull(authorization) && JwtUtil.verify(token, userName, passWord)) {
return true;
}
return false;
}

/**
* 获取登录用户
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,18 @@ public class CopyTokenFilter extends OncePerRequestFilter {
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// 以下为undertow定制代码,如切换其它servlet容器,需要同步更换
HttpServletRequestImpl undertowRequest = (HttpServletRequestImpl) request;
String bearerToken = request.getParameter("token");
String headerBearerToken = request.getHeader("X-Access-Token");
if (StringUtils.hasText(bearerToken)) {
undertowRequest.getExchange().getRequestHeaders().add(new HttpString("Authorization"), "bearer " + bearerToken);
} else if (StringUtils.hasText(headerBearerToken)) {
undertowRequest.getExchange().getRequestHeaders().add(new HttpString("Authorization"), "bearer " + headerBearerToken);
String token = request.getHeader("Authorization");
if (StringUtils.hasText(token)) {
undertowRequest.getExchange().getRequestHeaders().remove("Authorization");
undertowRequest.getExchange().getRequestHeaders().add(new HttpString("Authorization"), "bearer " + token);
} else {
String bearerToken = request.getParameter("token");
String headerBearerToken = request.getHeader("X-Access-Token");
if (StringUtils.hasText(bearerToken)) {
undertowRequest.getExchange().getRequestHeaders().add(new HttpString("Authorization"), "bearer " + bearerToken);
} else if (StringUtils.hasText(headerBearerToken)) {
undertowRequest.getExchange().getRequestHeaders().add(new HttpString("Authorization"), "bearer " + headerBearerToken);
}
}
filterChain.doFilter(undertowRequest, response);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.jeecg.config.security;

import lombok.RequiredArgsConstructor;
import org.jeecg.common.api.CommonAPI;
import org.jeecg.common.system.vo.LoginUser;
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.stereotype.Component;

import java.util.ArrayList;

/**
* token只存储用户名与过期时间
* 这里通过取用户名转全量用户信息存储到Security中
* @author [email protected]
* @date 2024/7/15 11:05
*/
@Component
@RequiredArgsConstructor
public class JeecgAuthenticationConvert implements Converter<Jwt, AbstractAuthenticationToken> {

private final CommonAPI commonAPI;

@Override
public AbstractAuthenticationToken convert(Jwt source) {
String username = source.getClaims().get("username").toString();
LoginUser loginUser = commonAPI.getUserByName(username);
return new UsernamePasswordAuthenticationToken(loginUser, null, new ArrayList<>());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package org.jeecg.config.security;

import org.jeecg.common.system.util.JwtUtil;
import org.springframework.lang.Nullable;
import org.springframework.security.oauth2.core.ClaimAccessor;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.jwt.JwsHeader;
import org.springframework.security.oauth2.jwt.JwtClaimsSet;
import org.springframework.security.oauth2.jwt.JwtEncoder;
import org.springframework.security.oauth2.jwt.JwtEncoderParameters;
import org.springframework.security.oauth2.server.authorization.OAuth2TokenType;
import org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationGrantAuthenticationToken;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.token.*;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.time.Duration;
import java.time.Instant;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalUnit;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
* @author [email protected]
* @date 2024/7/11 17:10
*/
public class JeecgOAuth2AccessTokenGenerator implements OAuth2TokenGenerator<OAuth2AccessToken> {
private final JwtEncoder jwtEncoder;

private OAuth2TokenCustomizer<OAuth2TokenClaimsContext> accessTokenCustomizer;

public JeecgOAuth2AccessTokenGenerator(JwtEncoder jwtEncoder) {
this.jwtEncoder = jwtEncoder;
}

@Nullable
@Override
public OAuth2AccessToken generate(OAuth2TokenContext context) {
if (!OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType())) {
return null;
}

String issuer = null;
if (context.getAuthorizationServerContext() != null) {
issuer = context.getAuthorizationServerContext().getIssuer();
}
RegisteredClient registeredClient = context.getRegisteredClient();

Instant issuedAt = Instant.now();
Instant expiresAt = issuedAt.plusMillis(JwtUtil.EXPIRE_TIME);

OAuth2TokenClaimsSet.Builder claimsBuilder = OAuth2TokenClaimsSet.builder();
if (StringUtils.hasText(issuer)) {
claimsBuilder.issuer(issuer);
}
claimsBuilder
.subject(context.getPrincipal().getName())
.audience(Collections.singletonList(registeredClient.getClientId()))
.issuedAt(issuedAt)
.expiresAt(expiresAt)
.notBefore(issuedAt)
.id(UUID.randomUUID().toString());
if (!CollectionUtils.isEmpty(context.getAuthorizedScopes())) {
claimsBuilder.claim(OAuth2ParameterNames.SCOPE, context.getAuthorizedScopes());
}

if (this.accessTokenCustomizer != null) {
OAuth2TokenClaimsContext.Builder accessTokenContextBuilder = OAuth2TokenClaimsContext.with(claimsBuilder)
.registeredClient(context.getRegisteredClient())
.principal(context.getPrincipal())
.authorizationServerContext(context.getAuthorizationServerContext())
.authorizedScopes(context.getAuthorizedScopes())
.tokenType(context.getTokenType())
.authorizationGrantType(context.getAuthorizationGrantType());
if (context.getAuthorization() != null) {
accessTokenContextBuilder.authorization(context.getAuthorization());
}
if (context.getAuthorizationGrant() != null) {
accessTokenContextBuilder.authorizationGrant(context.getAuthorizationGrant());
}

OAuth2TokenClaimsContext accessTokenContext = accessTokenContextBuilder.build();
this.accessTokenCustomizer.customize(accessTokenContext);
}


OAuth2TokenClaimsSet accessTokenClaimsSet = claimsBuilder.build();
OAuth2AuthorizationGrantAuthenticationToken oAuth2ResourceOwnerBaseAuthenticationToken = context.getAuthorizationGrant();
String username = (String) oAuth2ResourceOwnerBaseAuthenticationToken.getAdditionalParameters().get("username");
String tokenValue = jwtEncoder.encode(JwtEncoderParameters.from(JwsHeader.with(SignatureAlgorithm.ES256).keyId("jeecg").build(),
JwtClaimsSet.builder().claim("username", username).expiresAt(expiresAt).build())).getTokenValue();

//此处可以做改造将tokenValue随机数换成用户信息,方便后续多系统token互通认证(通过解密token得到username)
return new OAuth2AccessTokenClaims(OAuth2AccessToken.TokenType.BEARER, tokenValue,
accessTokenClaimsSet.getIssuedAt(), accessTokenClaimsSet.getExpiresAt(), context.getAuthorizedScopes(),
accessTokenClaimsSet.getClaims());
}

/**
* Sets the {@link OAuth2TokenCustomizer} that customizes the
* {@link OAuth2TokenClaimsContext#getClaims() claims} for the
* {@link OAuth2AccessToken}.
* @param accessTokenCustomizer the {@link OAuth2TokenCustomizer} that customizes the
* claims for the {@code OAuth2AccessToken}
*/
public void setAccessTokenCustomizer(OAuth2TokenCustomizer<OAuth2TokenClaimsContext> accessTokenCustomizer) {
Assert.notNull(accessTokenCustomizer, "accessTokenCustomizer cannot be null");
this.accessTokenCustomizer = accessTokenCustomizer;
}

private static final class OAuth2AccessTokenClaims extends OAuth2AccessToken implements ClaimAccessor {

private final Map<String, Object> claims;

private OAuth2AccessTokenClaims(TokenType tokenType, String tokenValue, Instant issuedAt, Instant expiresAt,
Set<String> scopes, Map<String, Object> claims) {
super(tokenType, tokenValue, issuedAt, expiresAt, scopes);
this.claims = claims;
}

@Override
public Map<String, Object> getClaims() {
return this.claims;
}

}
}
Loading

0 comments on commit f04f7f9

Please sign in to comment.