Szy*_*iak 13 spring-security spring-boot spring-security-oauth2
我有一个Spring Boot(1.2.1.RELEASE)应用程序,它在一个应用程序实例中提供OAuth2(2.0.6.RELEASE)授权和资源服务器.它使用自定义UserDetailsService实现MongoTemplate来搜索MongoDB中的用户.使用grant_type=passwordon进行身份验证/oauth/token就像魅力一样,以及Authorization: Bearer {token}在调用特定资源时使用标头进行授权.
现在我想向服务器添加简单的OAuth确认对话框,因此我可以在api-docs中对受保护资源进行身份验证和授权,例如Swagger UI调用.这是我到目前为止所做的:
@Configuration
@SessionAttributes("authorizationRequest")
class OAuth2ServerConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
registry.addViewController("/oauth/confirm_access").setViewName("authorize");
}
@Configuration
@Order(2)
protected static class LoginConfig extends WebSecurityConfigurerAdapter implements ApplicationEventPublisherAware {
@Autowired
UserDetailsService userDetailsService
@Autowired
PasswordEncoder passwordEncoder
ApplicationEventPublisher applicationEventPublisher
@Bean
DaoAuthenticationProvider daoAuthenticationProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider()
provider.passwordEncoder = passwordEncoder
provider.userDetailsService = userDetailsService
return provider
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.parentAuthenticationManager(authenticationManagerBean())
.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder())
}
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
//return super.authenticationManagerBean()
ProviderManager providerManager = new ProviderManager([daoAuthenticationProvider()], super.authenticationManagerBean())
providerManager.setAuthenticationEventPublisher(new DefaultAuthenticationEventPublisher(applicationEventPublisher))
return providerManager
}
@Bean
public PasswordEncoder passwordEncoder() {
new BCryptPasswordEncoder(5)
}
}
@Configuration
@EnableResourceServer
protected static class ResourceServer extends ResourceServerConfigurerAdapter {
@Value('${oauth.resourceId}')
private String resourceId
@Autowired
@Qualifier('authenticationManagerBean')
private AuthenticationManager authenticationManager
@Override
public void configure(HttpSecurity http) throws Exception {
http.setSharedObject(AuthenticationManager.class, authenticationManager)
http.csrf().disable()
http.httpBasic().disable()
http.formLogin().loginPage("/login").permitAll()
//http.authenticationProvider(daoAuthenticationProvider())
http.anonymous().and()
.authorizeRequests()
.antMatchers('/login/**').permitAll()
.antMatchers('/uaa/register/**').permitAll()
.antMatchers('/uaa/activate/**').permitAll()
.antMatchers('/uaa/password/**').permitAll()
.antMatchers('/uaa/account/**').hasAuthority('ADMIN')
.antMatchers('/api-docs/**').permitAll()
.antMatchers('/admin/**').hasAuthority('SUPERADMIN')
.anyRequest().authenticated()
//http.sessionManagement().sessionCreationPolicy(STATELESS)
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(resourceId)
resources.authenticationManager(authenticationManager)
}
}
@Configuration
@EnableAuthorizationServer
protected static class OAuth2Config extends AuthorizationServerConfigurerAdapter {
@Value('${oauth.clientId}')
private String clientId
@Value('${oauth.secret:}')
private String secret
@Value('${oauth.resourceId}')
private String resourceId
@Autowired
@Qualifier('authenticationManagerBean')
private AuthenticationManager authenticationManager
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
return new JwtAccessTokenConverter();
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.checkTokenAccess("permitAll()")
oauthServer.allowFormAuthenticationForClients()
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.accessTokenConverter(accessTokenConverter())
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient(clientId)
.secret(secret)
.authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
.authorities("USER", "ADMIN")
.scopes("read", "write", "trust")
.resourceIds(resourceId)
}
}
}
Run Code Online (Sandbox Code Playgroud)
主要问题是我不能同时运行(头文件中的Web登录表单和OAuth2授权令牌).如果ResourceServer获得更高的优先级,则OAuth2令牌授权有效,但我无法使用Web表单登录.另一方面,如果我将更高的优先级设置为LoginConfigclass,则OAuth2令牌授权将停止工作.
我发现在那种情况下,问题是由非注册引起的OAuth2AuthenticationProcessingFilter.我尝试在ResourceServer.configure(HttpSecurity http)方法中手动注册它,但它不起作用 - 我可以在FilterChain列表上看到过滤器,但它没有被触发.这不是修复它的好方法,因为在ResourceServer初始化期间还有很多其他的魔法,所以我转到了第二种情况.
在这种情况下,主要问题是默认情况下UsernamePasswordAuthenticationFilter找不到正确配置的AuthenticationProvider实例(in ProviderManager).当我尝试通过以下方式手动添加它时:
http.authenticationProvide(daoAuthenticationProvider())
Run Code Online (Sandbox Code Playgroud)
它得到一个,但在这种情况下,没有AuthenticationEventPublisher定义和成功的身份验证无法发布到其他组件.实际上在下一次迭代中它被替换为AnonymousAuthenticationToken.这就是我尝试AuthenticationManager用DaoAuthenticationProviderinside 定义手动实例的原因:
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
//return super.authenticationManagerBean()
ProviderManager providerManager = new ProviderManager([daoAuthenticationProvider()], super.authenticationManagerBean())
providerManager.setAuthenticationEventPublisher(new DefaultAuthenticationEventPublisher(applicationEventPublisher))
return providerManager
}
Run Code Online (Sandbox Code Playgroud)
我认为它会起作用,但是AuthenticationManager为注册过滤器提供实例会有一个不同的问题.事实证明,每个过滤器都authenticationManager使用sharedObjects组件手动注入:
authFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
Run Code Online (Sandbox Code Playgroud)
这里的问题是你不能保证有一个正确的实例集,因为有一个简单的HashMap(在GitHub上检查)用于存储特定的共享对象,它可以随时更改.我试着把它设置为:
http.setSharedObject(AuthenticationManager.class, authenticationManager)
Run Code Online (Sandbox Code Playgroud)
但是在我到达正在阅读的地方之前,它已经被默认实现所取代.我用调试器检查了它,看起来每个新过滤器都有一个新的身份验证管理器实例.
我的问题是:我做得对吗?如何使用集成在一个应用程序中的资源服务器和登录表单(OAuth2对话框)来设置授权服务器?也许它可以以一种不同的,更简单的方式完成.我会感谢任何帮助.
Szy*_*iak 11
这是问题的解决方案.看看这个典范Groovy课程:
@Configuration
@EnableResourceServer
class ResourceServer extends ResourceServerConfigurerAdapter {
@Value('${oauth.resourceId}')
private String resourceId
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
http.httpBasic().disable()
http.requestMatchers().antMatchers('/admin/**', '/uaa/**')
.and().authorizeRequests()
.antMatchers('/uaa/authenticated/**').authenticated()
.antMatchers('/uaa/register/**').permitAll()
.antMatchers('/uaa/activate/**').permitAll()
.antMatchers('/uaa/password/**').permitAll()
.antMatchers('/uaa/auth/**').permitAll()
.antMatchers('/uaa/account/**').hasAuthority('ADMIN')
.antMatchers('/admin/**').hasAuthority('ADMIN')
.anyRequest().authenticated()
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.resourceId(resourceId);
}
}
Run Code Online (Sandbox Code Playgroud)
基本上,要与Web表单身份验证并行运行OAuth2.0身份验证,您必须放置
http.requestMatchers().antMatchers('/path/1/**', '/path/2/**')
Run Code Online (Sandbox Code Playgroud)
到配置类.我之前的配置错过了这个重要部分,因此只有OAuth2.0参与了身份验证过程.
我认为您不应该尝试在您的设置中使用表单登录或http基本设置ResourceServerConfigurerAdapter,当然,如果您已经将它们放在其他设备中WebSecurityConfigurerAdapter,则不会这样(因为默认情况下它们处于打开状态).它可能有效,但对于受OAuth2保护的资源和UI,身份验证和访问决策是如此不同,我建议您将它们分开(因为它们在github中的所有示例中).如果您继续推荐并继续使用已定义的组件,那么获得此权限的关键是要知道过滤器链是按顺序尝试的,第一个是匹配获胜的,所以只有其中一个会对任何行为进行操作请求.您必须将请求匹配器放在两个链中(或至少是具有最低顺序的链),并确保它们不重叠.
| 归档时间: |
|
| 查看次数: |
16762 次 |
| 最近记录: |