请求地址: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 匹配不上
public boolean matches ( HttpServletRequest request) {
return requestMatcher. matches ( request) ;
}
1. 抽象过滤器:OncePerRequestFilter#doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
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) ;
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 谢谢