top.dcenter.ums.security.jwt.resolver.UmsBearerTokenResolver Maven / Gradle / Ivy
Show all versions of ums-jwt Show documentation
/*
* Copyright 2002-2020 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package top.dcenter.ums.security.jwt.resolver;
import org.springframework.http.HttpHeaders;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.server.resource.BearerTokenError;
import org.springframework.security.oauth2.server.resource.BearerTokenErrors;
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import top.dcenter.ums.security.common.utils.UrlUtil;
import javax.servlet.http.HttpServletRequest;
import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* The default {@link BearerTokenResolver} implementation based on RFC 6750.
*
* @author Vedran Pavic
* @author YongWu zheng
* @see RFC 6750
* Section 2: Authenticated Requests
* @since 5.1
*/
public final class UmsBearerTokenResolver implements BearerTokenResolver {
private static final Pattern AUTHORIZATION_PATTERN = Pattern.compile("^Bearer (?[a-zA-Z0-9-._~+/]+=*)$",
Pattern.CASE_INSENSITIVE);
private final String requestParameterName;
private boolean allowFormEncodedBodyParameter = false;
private boolean allowUriQueryParameter = false;
private String bearerTokenHeaderName = HttpHeaders.AUTHORIZATION;
/**
* 需要忽略的 url, 如: 通过 refreshToken 刷新 jwt uri, 登录的 uri. 支持 {@link AntPathMatcher} 模式.
*/
private final Set ignoreUrls = new HashSet<>();
private final AntPathMatcher matcher = new AntPathMatcher();
public UmsBearerTokenResolver(@NonNull String requestParameterName, @Nullable String jwtByRefreshTokenUri) {
this.requestParameterName = requestParameterName;
this.ignoreUrls.add(jwtByRefreshTokenUri);
}
@Override
public String resolve(HttpServletRequest request) {
final String pathWithinApplication = UrlUtil.getUrlPathHelper().getPathWithinApplication(request);
boolean anyMatch = ignoreUrls.stream()
.anyMatch((ignoreUrl) -> matcher.match(ignoreUrl,
pathWithinApplication));
// 如果是匹配 ignoreUrls 则返回 null
if (anyMatch) {
return null;
}
String authorizationHeaderToken = resolveFromAuthorizationHeader(request);
String parameterToken = resolveFromRequestParameters(request);
if (authorizationHeaderToken != null) {
if (parameterToken != null) {
BearerTokenError error = BearerTokenErrors
.invalidRequest("Found multiple bearer tokens in the request");
throw new OAuth2AuthenticationException(error);
}
return authorizationHeaderToken;
}
if (parameterToken != null && isParameterTokenSupportedForRequest(request)) {
return parameterToken;
}
return null;
}
/**
* 添加需要忽略解析 JWT token 的 url
*
* @param ignoreUrls 需要忽略解析 JWT token 的 urls, 支持 {@link AntPathMatcher} 模式.
*/
public void addIgnoreUrls(Set ignoreUrls) {
this.ignoreUrls.addAll(ignoreUrls);
}
/**
* Set if transport of access token using form-encoded body parameter is supported.
* Defaults to {@code false}.
*
* @param allowFormEncodedBodyParameter if the form-encoded body parameter is
* supported
*/
public void setAllowFormEncodedBodyParameter(boolean allowFormEncodedBodyParameter) {
this.allowFormEncodedBodyParameter = allowFormEncodedBodyParameter;
}
/**
* Set if transport of access token using URI query parameter is supported. Defaults
* to {@code false}.
*
* The spec recommends against using this mechanism for sending bearer tokens, and
* even goes as far as stating that it was only included for completeness.
*
* @param allowUriQueryParameter if the URI query parameter is supported
*/
public void setAllowUriQueryParameter(boolean allowUriQueryParameter) {
this.allowUriQueryParameter = allowUriQueryParameter;
}
/**
* Set this value to configure what header is checked when resolving a Bearer Token.
* This value is defaulted to {@link HttpHeaders#AUTHORIZATION}.
*
* This allows other headers to be used as the Bearer Token source such as
* {@link HttpHeaders#PROXY_AUTHORIZATION}
*
* @param bearerTokenHeaderName the header to check when retrieving the Bearer Token.
* @since 5.4
*/
public void setBearerTokenHeaderName(String bearerTokenHeaderName) {
this.bearerTokenHeaderName = bearerTokenHeaderName;
}
private String resolveFromAuthorizationHeader(HttpServletRequest request) {
String authorization = request.getHeader(this.bearerTokenHeaderName);
String bearer = "bearer";
if (!StringUtils.startsWithIgnoreCase(authorization, bearer)) {
return null;
}
Matcher matcher = AUTHORIZATION_PATTERN.matcher(authorization);
if (!matcher.matches()) {
BearerTokenError error = BearerTokenErrors.invalidToken("Bearer token is malformed");
throw new OAuth2AuthenticationException(error);
}
return matcher.group("token");
}
private String resolveFromRequestParameters(HttpServletRequest request) {
String[] values = request.getParameterValues(requestParameterName);
if (values == null || values.length == 0) {
return null;
}
if (values.length == 1) {
return values[0];
}
BearerTokenError error = BearerTokenErrors.invalidRequest("Found multiple bearer tokens in the request");
throw new OAuth2AuthenticationException(error);
}
private boolean isParameterTokenSupportedForRequest(HttpServletRequest request) {
return ((this.allowFormEncodedBodyParameter && "POST".equals(request.getMethod()))
|| (this.allowUriQueryParameter && "GET".equals(request.getMethod())));
}
}