Java Spring Security:401 Unauthorized for token OAuth2 端点

Posted

技术标签:

【中文标题】Java Spring Security:401 Unauthorized for token OAuth2 端点【英文标题】:Java Spring Security: 401 Unauthorized for token OAuth2 end point 【发布时间】:2019-02-21 16:00:26 【问题描述】:

我的 Spring Boot 项目中有一个相当基本的设置。我正在尝试设置 OAuth2 来保护我的 API,但我遇到了 /oauth/token 端点的问题。向我的/oauth/token 端点发出 POST 或 GET 请求会导致以下响应(带有401 Unauthorized 状态代码):


    "timestamp": "2018-09-17T16:46:59.961+0000",
    "status": 401,
    "error": "Unauthorized",
    "message": "Unauthorized",
    "path": "/oauth/token"

这是我的授权服务器配置。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.approval.UserApprovalHandler;
import org.springframework.security.oauth2.provider.token.TokenStore;

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter 
    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private UserApprovalHandler userApprovalHandler;

    @Autowired
    @Qualifier("authenticationManagerBean")
    private AuthenticationManager authenticationManager;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception 
        clients.inMemory()
                .withClient("client_id")
                .secret("secret")
                .authorizedGrantTypes("password", "authorization_code", "refresh_token")
                .scopes("read", "write")
                .accessTokenValiditySeconds(600)
                .refreshTokenValiditySeconds(3600);
    

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception 
        endpoints.tokenStore(this.tokenStore)
                .userApprovalHandler(this.userApprovalHandler)
                .authenticationManager(this.authenticationManager);
    

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) 
        security.tokenKeyAccess("permitAll()")
                .checkTokenAccess("isAuthenticated()")
                .passwordEncoder(this.passwordEncoder);
    

这是我的资源服务器配置。尚无重大意义:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;

public class ResourceServerConfig extends ResourceServerConfigurerAdapter 
    @Autowired
    private TokenStore tokenStore;

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception 
        resources.tokenStore(this.tokenStore);
    

最后是我的标准网络安全配置:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.TokenApprovalStore;
import org.springframework.security.oauth2.provider.approval.TokenStoreUserApprovalHandler;
import org.springframework.security.oauth2.provider.approval.UserApprovalHandler;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter 
    @Autowired
    private ClientDetailsService clientDetailsService;

    @Autowired
    public void globalUserDetails(AuthenticationManagerBuilder builder) throws Exception 
        builder.inMemoryAuthentication()
                .withUser("user").password("password").roles("ADMIN")
                .and()
                .withUser("admin").password("password").roles("USER");
    

    @Override
    protected void configure(HttpSecurity security) throws Exception 
        security.csrf().disable()
                .anonymous().disable()
                .authorizeRequests()
                .antMatchers("/oauth/token").permitAll();
    

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception 
        return super.authenticationManagerBean();
    

    @Bean
    public TokenStore tokenStore() 
        return new InMemoryTokenStore();
    

    @Bean
    public PasswordEncoder passwordEncoder() 
        return new BCryptPasswordEncoder(8);
    

    @Bean
    @Autowired
    public UserApprovalHandler userApprovalHandler(TokenStore tokenStore) 
        TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
        handler.setTokenStore(tokenStore);
        handler.setRequestFactory(new DefaultOAuth2RequestFactory(this.clientDetailsService));
        handler.setClientDetailsService(this.clientDetailsService);

        return handler;
    

    @Bean
    @Autowired
    public ApprovalStore approvalStore(TokenStore tokenStore) 
        TokenApprovalStore store = new TokenApprovalStore();
        store.setTokenStore(tokenStore);

        return store;
    

我已经用不同的匹配器模式搞砸了很多,看看我是否可以让它工作,但我没有任何运气。我在 http://localhost:8080 上的根上下文和 servlet 路径中运行它。

我可以确认,当 Spring Boot 启动并尝试命中稍微不同的端点会导致预期的 404 时,端点已映射到输出中。

【问题讨论】:

【参考方案1】:

原来我没有正确到达终点。我正在通过 HTTP POST 发送我的所有数据,包括客户端凭据。

POST http://localhost:8080/oauth/token
...
client_id=client_id&secret=secret&scope=read&grant_type=password&username=user&password=password

我需要使用 HTTP Basic Auth 来发送我的客户端凭据,而不是发布它们:

POST http://localhost:8080/oauth/token
Authorization: Basic Y2xpZW50X2lkOnNlY3JldA==
...
scope=read&grant_type=password&username=user&password=password

【讨论】:

如何使用邮递员发送 HTTP 基本身份验证? “授权”选项卡,从“类型”下拉菜单中选择“基本身份验证”,输入用户名/密码:learning.postman.com/docs/sending-requests/authorization/… 我发送此帖子请求“localhost:8080/oauth/…”,带有授权标头参数和“授权”选项卡中的基本身份验证?【参考方案2】:

尝试使用这个简单的编码器从AuthorizationServerConfig 类更改您的密码编码器(它不会加密密码)。因为您不会将客户端密码保存在 InMemory 存储中并进行加密。

private PasswordEncoder getPasswordEncoder() 
    return new PasswordEncoder() 
        public String encode (CharSequence charSequence) 
            return charSequence.toString();
        
        public boolean matches(CharSequence charSequence, String s) 
            return true;
        
    ;

希望它会起作用。

【讨论】:

感谢您的建议。我试了一下,但我仍然看到同样的问题。 事实证明,虽然这不是我当时的实际问题,但在我发现问题后,您的回答确实对我有所帮助,因为我收到了有关 BCrypt 的错误,所以谢谢。跨度>

以上是关于Java Spring Security:401 Unauthorized for token OAuth2 端点的主要内容,如果未能解决你的问题,请参考以下文章

Spring-Security:AuthenticationManager 抛出 BadCredentialsException 时返回状态 401

Spring Security 测试返回 401(未经授权)

Spring Boot / Spring Security中的错误401未经授权

Spring-Security:使用 CORS Preflights 获得 401(尽管 http.cors())

Spring Security - 401未经授权的访问

Spring Security 401 - 使用 / Angularjs 调用 restful 服务