【问题标题】:oauth/token Full authentication is required to access this resourceoauth/token 访问此资源需要完整的身份验证
【发布时间】:2018-08-14 17:56:06
【问题描述】:

我想以用户身份登录,但由于某种原因,端点 oauth/token 似乎受到保护:

Request URL:http://192.168.0.14:8080/oauth/token
Request Method:POST
Status Code:401 
Remote Address:192.168.0.14:8080
Referrer Policy:no-referrer-when-downgrade
Access-Control-Allow-Headers:x-requested-with, authorization, Content-Type, Authorization, credential, X-XSRF-TOKEN
Access-Control-Allow-Methods:PATCH,POST,GET,OPTIONS,DELETE
Access-Control-Allow-Origin:*
Access-Control-Max-Age:3600
Cache-Control:no-store
Cache-Control:no-cache, no-store, max-age=0, must-revalidate
Content-Type:application/json;charset=UTF-8
Date:Tue, 06 Mar 2018 18:59:25 GMT
Expires:0
Pragma:no-cache
Pragma:no-cache
Transfer-Encoding:chunked
WWW-Authenticate:Bearer realm="testjwtresourceid", error="unauthorized", error_description="Full authentication is required to access this resource"
WWW-Authenticate:Basic realm="oauth2/client"
X-Content-Type-Options:nosniff
X-Frame-Options:DENY
X-XSS-Protection:1; mode=block

有趣的是……我收到了这条消息:

2018-03-06 19:59:25.766  WARN 31030 --- [nio-8080-exec-2] o.s.s.c.bcrypt.BCryptPasswordEncoder     : Encoded password does not look like BCrypt

似乎出于某种原因,整个事情都贯穿了BasicAuthenticationFilter。很明显,这看起来不像 BCrypt .. 虽然我将 BCrypt 设置为我的密码编码器:

@Autowired
public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    auth.authenticationProvider(daoAuthenticationProvider());
}

/**
 * Using {@link BCryptPasswordEncoder} for user-password encryption.
 * @return
 */
@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}

看起来WebSecurity 尝试使用 BCrypt O_o 对客户端密码进行编码

security.jwt.client-id=CLIENT_ID
security.jwt.client-secret=CLIENT_SECRET

整个网络配置

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Value("${security.signing-key}")
    private String signingKey;

    @Value("${security.encoding-strength}")
    private Integer encodingStrength;

    @Value("${security.security-realm}")
    private String securityRealm;

    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    @Override
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    /**
     * Nothing to configure yet.
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception { }

    /**
     * Define routes for {@link WebSecurity}.
     *
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {

        final String[] SWAGGER_UI = {
                "/swagger-resources/**",
                "/swagger-ui.html",
                "/v2/api-docs",
                "/webjars/**"
        };

        web.ignoring().antMatchers("/pub/**", "/users")
                .antMatchers(SWAGGER_UI);
    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(signingKey);
        return converter;
    }

    /**
     * Using {@link JwtTokenStore} for JWT access tokens.
     * @return
     */
    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    /**
     * Provide {@link DefaultTokenServices} using the {@link JwtTokenStore}.
     * @return
     */
    @Bean
    public DefaultTokenServices tokenServices() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        defaultTokenServices.setSupportRefreshToken(true);
        return defaultTokenServices;
    }

    /**
     * We provide the AuthenticationManagerBuilder using our {@link UserDetailsService} and the {@link BCryptPasswordEncoder}.
     * @param auth
     * @throws Exception
     */
    @Autowired
    public void configureGlobalSecurity(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()).and()
            .authenticationProvider(daoAuthenticationProvider());
    }

    /**
     * Using {@link BCryptPasswordEncoder} for user-password encryption.
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * Provide {@link DaoAuthenticationProvider} for password encoding and set the {@link UserDetailsService}.
     * @return
     */
    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider() {
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder());
        daoAuthenticationProvider.setUserDetailsService(this.userDetailsService);
        return daoAuthenticationProvider;
    }

}

OAuth 配置

@Configuration
@EnableAuthorizationServer
@EnableResourceServer
public class OAuth2Configuration extends AuthorizationServerConfigurerAdapter {

    @Configuration
    @EnableResourceServer
    public class ResourceServer extends ResourceServerConfigurerAdapter {

        @Override
        public void configure(HttpSecurity http) throws Exception {
            http
                    .requestMatchers().antMatchers("/**")
                    .and()
                    .authorizeRequests().anyRequest().access("#oauth2.hasScope('write')");
        }

        @Override
        public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
            resources.resourceId(resourceIds);
        }

    }

    @Value("${security.jwt.client-id}")
    private String clientId;

    @Value("${security.jwt.client-secret}")
    private String clientSecret;

    @Value("${security.jwt.grant-type}")
    private String grantType;

    @Value("${security.jwt.scope-read}")
    private String scopeRead;

    @Value("${security.jwt.scope-write}")
    private String scopeWrite;

    @Value("${security.jwt.resource-ids}")
    private String resourceIds;

    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private JwtAccessTokenConverter accessTokenConverter;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Override
    public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {
        configurer
                .inMemory()
                .withClient(clientId)
                .secret(clientSecret)
                .authorizedGrantTypes(grantType)
                .scopes(scopeRead, scopeWrite)
                .resourceIds(resourceIds);
    }


    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
        enhancerChain.setTokenEnhancers(Arrays.asList(accessTokenConverter));
        endpoints.tokenStore(tokenStore)
                .accessTokenConverter(accessTokenConverter)
                .tokenEnhancer(enhancerChain)
                .authenticationManager(authenticationManager);
    }

}

【问题讨论】:

    标签: spring


    【解决方案1】:

    因为您创建了以下 Bean,所以所有安全配置都将使用这个PasswordEncoder。在您的情况下,WebSecurity 和 AuthorizationServerSecurity。

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    

    我很确定您的 security.jwt.client-secret 是纯文本。

    所以,发生的情况是,当您发出请求时,您的 AuthorizationSecurityConfig 尝试将 security.jwt.client-secret 读取为 BCrypt 编码字符串,但最终并非如此。

    因此,整个身份验证过程失败并记录警告消息。

    要解决此问题,您有两种选择:

    第一

    将 AuthorizationSecurity 配置为使用 NoOpPasswordEncoder。这是您可以覆盖它的方法。请注意NoOpPasswordEncoder 已被弃用。

    @Override
    public void configure(final AuthorizationServerSecurityConfigurer security) throws Exception {
        security.passwordEncoder(NoOpPasswordEncoder.getInstance());
    }
    

    第二(首选):

    生成security.jwt.client-secret 的BCrypt 值并将其设置在application.properties/yaml 中。

    感谢David 解决了对similar answer here 的疑问。

    猜猜看,当我在寻找解决方案时,我遇到了同样的问题。 :-)

    【讨论】:

    • 那行得通。正如你所推荐的,我选择了第二个选项!非常感谢!
    猜你喜欢
    • 2018-01-07
    • 2016-07-21
    • 2016-11-11
    • 2020-03-30
    • 2016-10-03
    • 2015-04-15
    • 2017-06-30
    • 2015-01-08
    • 2018-09-11
    相关资源
    最近更新 更多