【发布时间】:2015-08-07 21:17:36
【问题描述】:
我有一个关于 spring-security-oauth2 2.0.7 配置的问题。 我正在通过 GlobalAuthenticationConfigurerAdapter 使用 LDAP 进行身份验证:
@SpringBootApplication
@Controller
@SessionAttributes("authorizationRequest")
public class AuthorizationServer extends WebMvcConfigurerAdapter {
public static void main(String[] args) {
SpringApplication.run(AuthorizationServer.class, args);
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
registry.addViewController("/oauth/confirm_access").setViewName("authorize");
}
@Configuration
public static class JwtConfiguration {
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
KeyPair keyPair = new KeyStoreKeyFactory(
new ClassPathResource("keystore.jks"), "foobar".toCharArray())
.getKeyPair("test");
converter.setKeyPair(keyPair);
return converter;
}
@Bean
public JwtTokenStore jwtTokenStore(){
return new JwtTokenStore(jwtAccessTokenConverter());
}
}
@Configuration
@EnableAuthorizationServer
public static class OAuth2Config extends AuthorizationServerConfigurerAdapter implements EnvironmentAware {
private static final String ENV_OAUTH = "authentication.oauth.";
private static final String PROP_CLIENTID = "clientid";
private static final String PROP_SECRET = "secret";
private static final String PROP_TOKEN_VALIDITY_SECONDS = "tokenValidityInSeconds";
private RelaxedPropertyResolver propertyResolver;
@Inject
private AuthenticationManager authenticationManager;
@Inject
private JwtAccessTokenConverter jwtAccessTokenConverter;
@Inject
private JwtTokenStore jwtTokenStore;
@Inject
private UserDetailsService userDetailsService;
@Override
public void setEnvironment(Environment environment) {
this.propertyResolver = new RelaxedPropertyResolver(environment, ENV_OAUTH);
}
@Bean
@Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setSupportRefreshToken(true);
tokenServices.setTokenStore(jwtTokenStore);
tokenServices.setAuthenticationManager(authenticationManager);
return tokenServices;
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager).tokenStore(jwtTokenStore).accessTokenConverter(
jwtAccessTokenConverter).userDetailsService(userDetailsService);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer)
throws Exception {
oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess(
"isAuthenticated()");
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient(propertyResolver.getProperty(PROP_CLIENTID))
.scopes("read", "write")
.authorities(AuthoritiesConstants.ADMIN, AuthoritiesConstants.USER)
.authorizedGrantTypes("authorization_code", "refresh_token", "password")
.secret(propertyResolver.getProperty(PROP_SECRET))
.accessTokenValiditySeconds(propertyResolver.getProperty(PROP_TOKEN_VALIDITY_SECONDS, Integer.class, 1800));
}
}
@Configuration
@Order(-10)
protected static class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin().loginPage("/login").permitAll()
.and()
.requestMatchers().antMatchers("/login", "/oauth/authorize", "/oauth/confirm_access")
.and()
.authorizeRequests().anyRequest().authenticated();
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
@Override
public UserDetailsService userDetailsServiceBean() throws Exception {
return super.userDetailsServiceBean();
}
}
@Configuration
protected static class AuthenticationConfiguration extends
GlobalAuthenticationConfigurerAdapter {
@Override
public void init(AuthenticationManagerBuilder auth) throws Exception {
auth
.ldapAuthentication()
.userDnPatterns("uid={0},ou=people")
.groupSearchBase("ou=groups")
.contextSource().ldif("classpath:test-server.ldif");
}
}
}
虽然刷新令牌适用于 spring-security-oauth2 的 2.0.6 版,但它不再适用于 2.0.7 版。
如阅读here,应设置AuthenticationManager,以便在刷新期间尝试获取新的访问令牌时使用。
据我了解,这与 spring-security-oauth2 的 following 更改有关。
很遗憾,我没有设法正确设置它。
org.springframework.security.oauth2.provider.token.DefaultTokenServices#setAuthenticationManager
被调用并获得AuthenticationManager 注入。我不确定我是否理解LdapUserDetailsService 将如何被注入。我唯一看到的是在令牌刷新调用期间尝试重新验证用户身份时将调用PreAuthenticatedAuthenticationProvider。
谁能告诉我怎么做?
ps:我得到的异常如下:
p.PreAuthenticatedAuthenticationProvider : PreAuthenticated authentication request: org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken@5775: Principal: org.springframework.security.authentication.UsernamePasswordAuthenticationToken@441d5545: Principal: bob; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_USER; Credentials: [PROTECTED]; Authenticated: true; Details: null; Granted Authorities: ROLE_USER
o.s.s.o.provider.endpoint.TokenEndpoint : Handling error: IllegalStateException, UserDetailsService is required.
【问题讨论】:
-
如here、
userDetailsService: if you inject a UserDetailsService or if one is configured globally anyway (e.g. in a GlobalAuthenticationManagerConfigurer) then a refresh token grant will contain a check on the user details, to ensure that the account is still active.所述,我希望LdapUserDetailsService会被自动使用。 -
我也会(它对我有用)。你能发布一个完整的项目吗?
-
@dave-syer,非常感谢您的回复。我在以下测试项目中隔离了问题:github.com/jhoelter/zaas/tree/master/authserver 有一个名为 spring-security-oauth2-2.0.6 的标签,其中刷新令牌工作正常。当切换到 2.0.7 并设置上述配置时,它会崩溃。非常感谢您的帮助
-
好的,我看到了问题。
LdapAuthenticationProvider不受UserDetailsService的支持,因此您实际上在任何地方都没有(过滤器链中的空委托除外)。我会尝试一下,看看是否有解决方法,或者我们可以添加的新功能。 -
我也遇到了同样的问题,Spring 框架简直就是一场噩梦!因为我使用的是 AuthenticationProvider 实现而不是 UserDetailService,但是如果我像 Jereremie 所说的那样将库降级到 2.0.6,它可以工作,但是现在使用
grant_type=user_credentials登录不起作用,Spring 不会映射权限而且我不知道为什么,因为角色在那里,当您解码令牌时可以使用任何 JWT 工具查看。
标签: spring spring-security spring-security-oauth2 spring-security-ldap