Spring Boot OAuth2 + JWT 和 UserDetailsService
Posted
技术标签:
【中文标题】Spring Boot OAuth2 + JWT 和 UserDetailsService【英文标题】: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 对象(在 DBUserDetailsService.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 和 UserDetailsService的主要内容,如果未能解决你的问题,请参考以下文章
带有加密 JWT 访问令牌的 Spring Boot OAuth2
Spring Boot Security OAuth2 实现支持JWT令牌的授权服务器