问题描述
尝试让UserDetailsService适用于我设置的oauth2资源服务器。我能够成功验证jwt,但似乎没有任何办法让它调用loadUserByUsername方法。最初使用SAML并可以正常工作,但是现在我切换到了Oauth2,但无法正常工作。
@Service
public class OauthUsersDetailsServiceImpl implements UserDetailsService{
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//some user loading junk here - this is never called
}
}
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception
{
//test key for Now
SecretKeySpec key = new SecretKeySpec("private key0000000000000000000000000000000".getBytes(),"HMACSHA256");
http
.authorizeRequests()
.antMatchers(/*some endpoints im excluding from auth - this all works*/)
.permitAll().and()
.authorizeRequests()
.anyRequest().authenticated().and()
.oauth2ResourceServer().jwt().decoder(NimbusJwtDecoder.withSecretKey(key).build());
}
}
我在Google上发现我可以使用@service将类注册为Bean,而spring只会选择它,但是它不起作用。我也尝试通过AuthenticationManagerBuilder添加它,但这也不起作用。我的猜测是,它的jwt端具有其自己的UserDetailsService,该UserDetailsService已实现,并且优先于我的。也就是说,什么是让我的呼叫正确的方法,还是在身份验证完成并覆盖Principal对象之后以某种方式手动调用用户加载逻辑更好?我需要在调用端点之前进行此操作,以便PreAuthorize可以检查UserDetailsService加载的角色。
解决方法
弄清楚了。希望这将对遇到相同问题的任何人有所帮助。我必须在链中添加一个自定义过滤器,以调用我的用户详细信息服务并覆盖上下文:
public class Oauth2AuthorizationFilter extends GenericFilterBean {
@Autowired
private OauthUsersDetailsServiceImpl oauthUsersDetailsServiceImpl;
public Oauth2AuthorizationFilter (OauthUsersDetailsServiceImpl userDetailsService) {
this.oauthUsersDetailsServiceImpl = userDetailsService;
}
@Override
public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain)
throws IOException,ServletException {
SecurityContext context = SecurityContextHolder.getContext();
if(context.getAuthentication() != null && !(context.getAuthentication().getPrincipal() instanceof Users)) {
UserDetails user = oauthUsersDetailsServiceImpl.loadUserByUsername(((Jwt)context.getAuthentication().getPrincipal()).getClaimAsString("user_name"));
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(user,null,user.getAuthorities());
context.setAuthentication(authentication);
}
chain.doFilter(request,response);
}
}
@Override
protected void configure(HttpSecurity http) throws Exception
{
//test key for now
SecretKeySpec key = new SecretKeySpec("private key0000000000000000000000000000000".getBytes(),"HMACSHA256");
http.authorizeRequests().antMatchers(/*bunch of junk...*/).permitAll().and().authorizeRequests().anyRequest().authenticated().and()
.oauth2ResourceServer().jwt().decoder(NimbusJwtDecoder.withSecretKey(key).build());
http.addFilterAfter(jwtAuthTokenFilterBean(),SwitchUserFilter.class);
}
那终于做到了我所需要的
,您需要注册UserDetailsService实现,然后DaoAuthenticationProvider会使用该实现
// userDetailsService bean
@Autowired
private OauthUsersDetailsServiceImpl oauthUsersDetailsServiceImpl;
//
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(oauthUsersDetailsServiceImpl);
}