Spring Boot OAuth2 + JWT 和 UserDetailsS​​ervice

Posted

技术标签:

【中文标题】Spring Boot OAuth2 + JWT 和 UserDetailsS​​ervice【英文标题】:Spring Boot OAuth2 + JWT and UserDetailsService 【发布时间】:2017-01-31 12:24:09 【问题描述】:

在我的 Spring Boot 应用程序中,我正在尝试配置 Spring OAuth2 + JWT

这是我的OAuth2ServerConfig 配置:

@Configuration
public class OAuth2ServerConfig 

    private static final String RESOURCE_ID = "restservice";

    @Bean
    @Primary
    public DefaultTokenServices tokenServices() 
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        defaultTokenServices.setSupportRefreshToken(true);
        return defaultTokenServices;
    

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() 
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey("123");
        return converter;
    

    @Bean
    public TokenStore tokenStore() 
        return new JwtTokenStore(accessTokenConverter());
    

    @Configuration
    @EnableAuthorizationServer
    protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter 

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

        @Autowired
        private TokenStore tokenStore;

        @Autowired
        private TokenEnhancer tokenEnhancer;

        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception 
            // @formatter:off
            endpoints
                .tokenStore(tokenStore)
                .tokenEnhancer(tokenEnhancer)
                .authenticationManager(this.authenticationManager);
            // @formatter:on
        

        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception 
            // @formatter:off
            clients
                .inMemory()
                    .withClient("clientapp")
                        .authorizedGrantTypes("password","refresh_token")
                        .authorities("ROLE_CLIENT")
                        .scopes("read", "write")
                        .resourceIds(RESOURCE_ID)
                        .secret("123456");
            // @formatter:on
        

    

    @Configuration
    @EnableResourceServer
    protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter 

        @Autowired
        private ResourceServerTokenServices tokenService;

        @Override
        public void configure(ResourceServerSecurityConfigurer resources) 
            // @formatter:off
            resources           
                .resourceId(RESOURCE_ID)
                .tokenServices(tokenService);
            // @formatter:on
        

        @Override
        public void configure(HttpSecurity http) throws Exception 
            // @formatter:off
            http
                .authorizeRequests()
                    .antMatchers("/profile/*").authenticated()
                    .and().csrf()
                    .disable().sessionManagement().sessionCreationPolicy(STATELESS);
            // @formatter:on
        

    


这是我的UserDetailsService 实现:

@Service
public class DBUserDetailsService implements UserDetailsService 

    @Autowired
    private UserService userService;

    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException 
        User user = userService.findUserByUsername(username);
        if (user == null) 
            throw new UsernameNotFoundException("User " + username + " not found.");
        

        Set<Permission> permissions = userService.getUserPermissions(user);
        return new DBUserDetails(user, permissions);
    


这是WebSecurityConfig

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter 

    @Autowired
    private SocialAuthenticationSuccessHandler socialAuthenticationSuccessHandler;

    @Autowired
    private DBUserDetailsService userDetailsService;

    @Value("$social.postLogin.url")
    private String postLoginUrl;

    @Override
    public void configure(WebSecurity web) throws Exception 
        // Spring Security ignores request to static resources such as CSS or JS
        // files.
        web.ignoring().antMatchers("/static/**");
    


    @Override
    protected void configure(HttpSecurity http) throws Exception 

        // @formatter:off
        http.addFilterBefore(new CorsFilter(), ChannelProcessingFilter.class);

        // Set a custom successHandler on the SocialAuthenticationFilter
        final SpringSocialConfigurer socialConfigurer = new SpringSocialConfigurer();
        socialConfigurer.addObjectPostProcessor(new ObjectPostProcessor<SocialAuthenticationFilter>() 
            @Override
            public <O extends SocialAuthenticationFilter> O postProcess(O socialAuthenticationFilter) 
                socialAuthenticationFilter.setAuthenticationSuccessHandler(socialAuthenticationSuccessHandler);
                socialAuthenticationFilter.setPostLoginUrl(postLoginUrl);
                return socialAuthenticationFilter;
            
        );

        http.csrf().disable().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        //Configures url based authorization
        .and()
            .authorizeRequests()
            //Anyone can access the urls
            .antMatchers("/auth/**").permitAll()
            .antMatchers("/actuator/health").permitAll()
            .antMatchers("/actuator/**").hasAuthority("PERMISSION_READ_ACTUATOR_DATA")
        //Adds the SocialAuthenticationFilter to Spring Security's filter chain.
        .and()
            // apply the configuration from the socialConfigurer (adds the SocialAuthenticationFilter)
            .apply(socialConfigurer);

        // @formatter:on
    

    /**
     * Configures the authentication manager bean which processes authentication
     * requests.
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception 
        auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
    

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


现在我可以成功地发出 JWT accessToken,但是当我尝试使用它时,我现有的逻辑失败并出现以下错误:

java.lang.String cannot be cast to com.example.domain.model.security.DBUserDetails

由于某种原因,DBUserDetailsService.loadUserByUsername 在基于 JWT 令牌的身份验证成功后不会被调用,而不是在 SecurityContextHolder.getContext().getAuthentication().getPrincipal() 中使用 DBUserDetails 我只有一个带有用户名的字符串,例如“admin”

我做错了什么?

【问题讨论】:

据我所知,什么都没有。似乎主体设置正确。您还期望Authentication 对象还有什么? 我期待 DBUserDetails 对象(在 DBUserDetailsS​​ervice.loadUserByUsername 方法调用之后),就像没有 JWT 令牌的纯 OAuth2 配置一样 【参考方案1】:

在 Spring 源代码调试之后,我找到了一个解决方案,如何将我的 UserDetailsService 注入流中。为此,您必须修改 accessTokenConverter() 方法:

@Autowired
private DBUserDetailsService userDetailsService;

@Bean
public JwtAccessTokenConverter accessTokenConverter() 
    JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
    converter.setSigningKey("123");

    DefaultAccessTokenConverter accessTokenConverter = new DefaultAccessTokenConverter();
    DefaultUserAuthenticationConverter userTokenConverter = new DefaultUserAuthenticationConverter();
    userTokenConverter.setUserDetailsService(userDetailsService);
    accessTokenConverter.setUserTokenConverter(userTokenConverter);

    converter.setAccessTokenConverter(accessTokenConverter);

    return converter;

【讨论】:

以上是关于Spring Boot OAuth2 + JWT 和 UserDetailsS​​ervice的主要内容,如果未能解决你的问题,请参考以下文章

Oauth2 + jwt 和 spring boot

Spring Boot 2 和 OAuth2/JWT 配置

带有加密 JWT 访问令牌的 Spring Boot OAuth2

Spring Boot Security OAuth2 实现支持JWT令牌的授权服务器

Spring Boot OAuth2 + JWT 和 UserDetailsS​​ervice

如何在 Spring Boot 中实现 oAuth2 和 JWT 身份验证? [关闭]