oauth2源码级别解析报错原因:There is no client authentication. Try adding an appropriate authentication filter
【代码】oauth2源码级别解析报错原因:There is no client authentication. Try adding an appropriate authentication filter。
·
请求地址:localhost:40150/oauth/token/?grant_type=mobile_password
请求头, Basic 是 client_id 和 client_secret
请求参数
TokenEndpoint#postAccessToken 报错There is no client authentication. Try adding an appropriate authentication filter.
@RequestMapping(value = "/oauth/token", method=RequestMethod.POST)
public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal,
@RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
if (!(principal instanceof Authentication)) {
throw new InsufficientAuthenticationException(
"There is no client authentication. Try adding an appropriate authentication filter.");
}
// 省略代码
...
}
报错原因:/oauth/token/ 改成 /oauth/token, 因为结尾多了斜杆 / 导致
DefaultSecurityFilterChain#matches 匹配不上
/**
DefaultSecurityFilterChain#matches
*/
public boolean matches(HttpServletRequest request) {
return requestMatcher.matches(request);
}
1. 抽象过滤器:OncePerRequestFilter#doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
// 找到这个方法 this = WebMvcMetricsFilter
this.doFilterInternal(httpRequest, httpResponse, filterChain);
2. ApplicationFilterChain#doFilter 调用 internalDoFilter
@Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if( Globals.IS_SECURITY_ENABLED ) {
...
}else {
internalDoFilter(request,response);
}
}
internalDoFilter 取出过滤器 调用 FilterChainProxy#doFilter
2. FilterChainProxy#doFilter 方法里面调用 doFilterInternal 关键方法 getFilters 这里返回具体的过滤器
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
doFilterInternal(request, response, chain);
}
private void doFilterInternal(ServletRequest request, ServletResponse response, FilterChain chain) {
// 获取过滤器
List<Filter> filters = getFilters(fwRequest);
}
private List<Filter> getFilters(HttpServletRequest request) {
for (SecurityFilterChain chain : filterChains) {
if (chain.matches(request)) {
return chain.getFilters();
}
}
return null;
}
WebSecurity#performBuild 把 FilterChainProxy.filterChains 初始化, 如下图,最关键是 filters
看到有两个实现类, 里面的filters,那到底选择哪个filters, 又是如何选择的
FilterChainProxy#getFilters, 遍历 filterChains,根据request 匹配正则来确定选 filters
private List<Filter> getFilters(HttpServletRequest request) {
for (SecurityFilterChain chain : filterChains) {
if (chain.matches(request)) {
return chain.getFilters();
}
}
return null;
}
重点来了,报错根本原因就是这个 chain.matches(request) 我的请求 request = /oauth/token/, OrRequestMatcher [requestMatchers=[Ant [pattern=‘/oauth/token’], Ant [pattern=‘/oauth/token_key’], Ant [pattern=‘/oauth/check_token’]]] OrRequestMatcher.requestMatchers [Ant [pattern=‘/oauth/token’]],导致request = /oauth/token/ 和 pattern='/oauth/token’匹配不上 选错了filters, 具体看图
OrRequestMatcher#matches, 返回 false, 进入下一个循环
public boolean matches(HttpServletRequest request) {
for (RequestMatcher matcher : requestMatchers) {
if (logger.isDebugEnabled()) {
logger.debug("Trying to match using " + matcher);
}
if (matcher.matches(request)) {
logger.debug("matched");
return true;
}
}
logger.debug("No matches found");
return false;
}
AnyRequestMatcher#matches 直接返回true
public boolean matches(HttpServletRequest request) {
return true;
}
FilterChainProxy#doFilterInternal 关键的类 VirtualFilterChain,把刚filters 设置到里面,
private void doFilterInternal(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
...
VirtualFilterChain vfc = new VirtualFilterChain(fwRequest, chain, filters);
// 调用doFilter, 然后挨个匹配 filters 列表的filter, 找到对应的
vfc.doFilter(fwRequest, fwResponse);
}
VirtualFilterChain#doFilter, 把 filters 的过滤器匹配
@Override
public void doFilter(ServletRequest request, ServletResponse response) {
if (currentPosition == size) {
...
} else {
currentPosition++;
Filter nextFilter = additionalFilters.get(currentPosition - 1);
nextFilter.doFilter(request, response, this);
}
}
AnyRequestMatcher对应的filters
[
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter,
org.springframework.security.web.context.SecurityContextPersistenceFilter,
org.springframework.security.web.header.HeaderWriterFilter,
org.springframework.security.web.authentication.logout.LogoutFilter,
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter,
org.springframework.security.web.savedrequest.RequestCacheAwareFilter,
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter,
org.springframework.security.web.authentication.AnonymousAuthenticationFilter,
org.springframework.security.web.session.SessionManagementFilter,
org.springframework.security.web.access.ExceptionTranslationFilter,
org.springframework.security.web.access.intercept.FilterSecurityInterceptor
]
OrRequestMatcher对应的filters
[
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter,
org.springframework.security.web.context.SecurityContextPersistenceFilter,
org.springframework.security.web.header.HeaderWriterFilter,
org.springframework.security.web.authentication.logout.LogoutFilter,
org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter,
org.springframework.security.web.authentication.www.BasicAuthenticationFilter,
org.springframework.security.web.savedrequest.RequestCacheAwareFilter,
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter,
org.springframework.security.web.authentication.AnonymousAuthenticationFilter,
org.springframework.security.web.session.SessionManagementFilter,
org.springframework.security.web.access.ExceptionTranslationFilter,
org.springframework.security.web.access.intercept.FilterSecurityInterceptor
]
因为我请求 Basic 要选择 BasicAuthenticationFilter处理才是正确的,也因为 request = /oauth/token/ 和 pattern='/oauth/token’匹配不上选错了filters,导致找不到对应的filter 处理,最终导致 TokenEndpoint的请求参数principal 为空 抛出 throw new InsufficientAuthenticationException(“There is no client authentication. Try adding an appropriate authentication filter.”); 至此报错原因,源码级别分析到此结束
@RequestMapping(value = "/oauth/token", method=RequestMethod.POST)
public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal,
@RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
if (!(principal instanceof Authentication)) {
throw new InsufficientAuthenticationException(
"There is no client authentication. Try adding an appropriate authentication filter.");
}
// 省略代码
...
}
断点的类
end 谢谢
更多推荐
已为社区贡献1条内容
所有评论(0)