Oauth2校验jwt的过期时间源码
前述
在
security-cloud-oauth2
代码中,并没有看见校验Token
过期的地方,经过查阅资料以及Debug
,发现其实oauth2
已经帮我们做了。
JwtReactiveAuthenticationManager
oauth2
在JwtReactiveAuthenticationManager
中就已经帮我们写好了校验token
的逻辑,具体方法为:authenticate
,在该方法中进行了客户端token
的解析校验
public final class JwtReactiveAuthenticationManager implements ReactiveAuthenticationManager {
private final ReactiveJwtDecoder jwtDecoder;
private Converter<Jwt, ? extends Mono<? extends AbstractAuthenticationToken>> jwtAuthenticationConverter = new ReactiveJwtAuthenticationConverterAdapter(
new JwtAuthenticationConverter());
public JwtReactiveAuthenticationManager(ReactiveJwtDecoder jwtDecoder) {
Assert.notNull(jwtDecoder, "jwtDecoder cannot be null");
this.jwtDecoder = jwtDecoder;
}
// 在JwtReactiveAuthenticationManager#authenticate方法里进行了客户端token的解析校验。
@Override
public Mono<Authentication> authenticate(Authentication authentication) {
// @formatter:off
return Mono.justOrEmpty(authentication)
.filter((a) -> a instanceof BearerTokenAuthenticationToken)
.cast(BearerTokenAuthenticationToken.class)
.map(BearerTokenAuthenticationToken::getToken)
.flatMap(this.jwtDecoder::decode)
.flatMap(this.jwtAuthenticationConverter::convert)
.cast(Authentication.class)
.onErrorMap(JwtException.class, this::onError);
// @formatter:on
}
}
JwtReactiveAuthenticationManager
的authenticate
方法,先是从authentication
获取了加密的token
,继而使用ReactiveJwtDecoder
的decode
去解析它.
@FunctionalInterface
public interface ReactiveJwtDecoder {
/**
* Decodes the JWT from it's compact claims representation format and returns a
* {@link Jwt}.
* @param token the JWT value
* @return a {@link Jwt}
* @throws JwtException if an error occurs while attempting to decode the JWT
*/
Mono<Jwt> decode(String token) throws JwtException;
}
ReactiveJwtDecoder
有两个实现类:SupplierReactiveJwtDecoder
和NimbusReactiveJwtDecoder
,经Debug
发现NimbusReactiveJwtDecoder
才是我们需要去了解的。
NimbusReactiveJwtDecoder
- 在
NimbusReactiveJwtDecoder
中,decode
方法首先调用JWTParser.parse(token)
解出明文成JWT
,然后调用validateJwt
方法验证解析出来的JWT
public final class NimbusReactiveJwtDecoder implements ReactiveJwtDecoder {
private final Converter<JWT, Mono<JWTClaimsSet>> jwtProcessor;
private OAuth2TokenValidator<Jwt> jwtValidator = JwtValidators.createDefault();
private Converter<Map<String, Object>, Map<String, Object>> claimSetConverter = MappedJwtClaimSetConverter
.withDefaults(Collections.emptyMap());
@Override
public Mono<Jwt> decode(String token) throws JwtException {
// 调用JWTParser.parse(token)解出明文成JWT
JWT jwt = parse(token);
if (jwt instanceof PlainJWT) {
throw new BadJwtException("Unsupported algorithm of " + jwt.getHeader().getAlgorithm());
}
return this.decode(jwt);
}
private JWT parse(String token) {
try {
return JWTParser.parse(token);
}
catch (Exception ex) {
throw new BadJwtException("An error occurred while attempting to decode the Jwt: " + ex.getMessage(), ex);
}
}
// decode调用validateJwt方法验证解析出来的JWT
private Mono<Jwt> decode(JWT parsedToken) {
try {
// @formatter:off
return this.jwtProcessor.convert(parsedToken)
.map((set) -> createJwt(parsedToken, set))
.map(this::validateJwt)
.onErrorMap((ex) -> !(ex instanceof IllegalStateException) && !(ex instanceof JwtException),
(ex) -> new JwtException("An error occurred while attempting to decode the Jwt: ", ex));
// @formatter:on
}
catch (JwtException ex) {
throw ex;
}
catch (RuntimeException ex) {
throw new JwtException("An error occurred while attempting to decode the Jwt: " + ex.getMessage(), ex);
}
}
// 此处为校验token
private Jwt validateJwt(Jwt jwt) {
OAuth2TokenValidatorResult result = this.jwtValidator.validate(jwt);
if (result.hasErrors()) {
Collection<OAuth2Error> errors = result.getErrors();
String validationErrorString = getJwtValidationExceptionMessage(errors);
throw new JwtValidationException(validationErrorString, errors);
}
return jwt;
}
}
OAuth2TokenValidator
如上图OAuth2TokenValidator
校验JWT
有五个实现类,这意味着有多个维度的校验,这里以时间戳的校验进行查看(即过期时间的校验)
JwtTimestampValidator
public final class JwtTimestampValidator implements OAuth2TokenValidator<Jwt> {
private final Log logger = LogFactory.getLog(getClass());
private static final Duration DEFAULT_MAX_CLOCK_SKEW = Duration.of(60, ChronoUnit.SECONDS);
private final Duration clockSkew;
private Clock clock = Clock.systemUTC();
/**
* A basic instance with no custom verification and the default max clock skew
*/
public JwtTimestampValidator() {
this(DEFAULT_MAX_CLOCK_SKEW);
}
public JwtTimestampValidator(Duration clockSkew) {
Assert.notNull(clockSkew, "clockSkew cannot be null");
this.clockSkew = clockSkew;
}
@Override
public OAuth2TokenValidatorResult validate(Jwt jwt) {
Assert.notNull(jwt, "jwt cannot be null");
// 过期时间点
Instant expiry = jwt.getExpiresAt();
if (expiry != null) {
// isAfter 表示在过期时间点之后
// this.clockSkew:springsecurity在校验过期时间的时候会在当前时间上减去这个时间偏移,
// 默认是60秒,再和你设定的应该过期的时间点比较。计算后的时间节点如果在你设定的时间之后才会进入if,返回token失效。
// (例如,当前时间8:00,你设定一分钟过期,实际判定需要等到8:02才会返回token失效)。
if (Instant.now(this.clock).minus(this.clockSkew).isAfter(expiry)) {
OAuth2Error oAuth2Error = createOAuth2Error(String.format("Jwt expired at %s", jwt.getExpiresAt()));
return OAuth2TokenValidatorResult.failure(oAuth2Error);
}
}
Instant notBefore = jwt.getNotBefore();
if (notBefore != null) {
if (Instant.now(this.clock).plus(this.clockSkew).isBefore(notBefore)) {
OAuth2Error oAuth2Error = createOAuth2Error(String.format("Jwt used before %s", jwt.getNotBefore()));
return OAuth2TokenValidatorResult.failure(oAuth2Error);
}
}
return OAuth2TokenValidatorResult.success();
}
private OAuth2Error createOAuth2Error(String reason) {
this.logger.debug(reason);
return new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN, reason,
"https://tools.ietf.org/html/rfc6750#section-3.1");
}
/**
* Use this {@link Clock} with {@link Instant#now()} for assessing timestamp validity
* @param clock
*/
public void setClock(Clock clock) {
Assert.notNull(clock, "clock cannot be null");
this.clock = clock;
}
}