我正在尝试使用受Oauth2或Http-Basic身份验证保护的资源来实现API。
当我加载首先对资源应用http-basic身份验证的WebSecurityConfigurerAdapter时,不接受Oauth2令牌身份验证。反之亦然。
示例配置: 这将http-basic身份验证应用于所有/ user / **资源
@Configuration
@EnableWebMvcSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private LoginApi loginApi;
@Autowired
public void setLoginApi(LoginApi loginApi) {
this.loginApi = loginApi;
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new PortalUserAuthenticationProvider(loginApi));
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/users/**").authenticated()
.and()
.httpBasic();
}
@Override
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
这将oauth令牌保护应用于/ user / **资源
@Configuration
@EnableResourceServer
protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http
.requestMatchers().antMatchers("/users/**")
.and()
.authorizeRequests()
.antMatchers("/users/**").access("#oauth2.clientHasRole('ROLE_CLIENT') and #oauth2.hasScope('read')");
}
}
我确定有一些我遗漏的魔法代码会告诉spring如果第一次失败会尝试两者?
非常感谢任何帮助。
答案 0 :(得分:29)
我设法根据Michael Ressler的回答提示这项工作,但进行了一些调整。
我的目标是在同一资源端点上允许Basic Auth和Oauth,例如/ leafcase / 123。由于filterChains的排序,我被困了很长时间(可以在FilterChainProxy.filterChains中检查);默认顺序如下:
由于资源服务器的filterChains排名高于WebSecurityConfigurerAdapter配置的filterchain,并且前者几乎匹配每个资源端点,因此Oauth资源服务器逻辑总是会对资源端点的任何请求启动(即使请求使用Authorization:Basic)头)。你会得到的错误是:
{
"error": "unauthorized",
"error_description": "Full authentication is required to access this resource"
}
我做了2次更改才能完成这项工作:
首先,订购WebSecurityConfigurerAdapter高于资源服务器(订单2高于订单3)。
@Configuration
@Order(2)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
其次,让configure(HttpSecurity)使用仅匹配“Authorization:Basic”的客户RequestMatcher。
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.anonymous().disable()
.requestMatcher(new BasicRequestMatcher())
.authorizeRequests()
.anyRequest().authenticated()
.and()
.httpBasic()
.authenticationEntryPoint(oAuth2AuthenticationEntryPoint())
.and()
// ... other stuff
}
...
private static class BasicRequestMatcher implements RequestMatcher {
@Override
public boolean matches(HttpServletRequest request) {
String auth = request.getHeader("Authorization");
return (auth != null && auth.startsWith("Basic"));
}
}
因此,在资源服务器的filterChain有机会匹配它之前,它会匹配并处理Basic Auth资源请求。它还只处理Authorizaiton:基本资源请求,因此任何具有Authorization:Bearer的请求都将通过,然后由资源服务器的filterChain处理(即,Oauth的过滤器启动)。此外,它的排名低于AuthenticationServer(如果在同一个项目中启用了AuthenticationServer),因此它不会阻止AuthenticaitonServer的过滤链处理对/ oauth / token等的请求。
答案 1 :(得分:5)
这可能接近你所寻找的:
@Override
public void configure(HttpSecurity http) throws Exception {
http.requestMatcher(new OAuthRequestedMatcher())
.authorizeRequests()
.anyRequest().authenticated();
}
private static class OAuthRequestedMatcher implements RequestMatcher {
@Override
public boolean matches(HttpServletRequest request) {
String auth = request.getHeader("Authorization");
// Determine if the client request contained an OAuth Authorization
return (auth != null) && auth.startsWith("Bearer");
}
}
唯一没有提供的是退回"如果身份验证不成功。
对我而言,这种方法是有道理的。如果用户通过基本身份验证直接向请求提供身份验证,则无需OAuth。如果客户端是代理客户端,那么我们需要此过滤器进入并确保请求已正确验证。
答案 2 :(得分:2)
您可以将BasicAuthenticationFilter添加到安全筛选器链,以在受保护资源上获取OAuth2 OR Basic身份验证安全性。示例配置如下......
@Configuration
@EnableResourceServer
public class OAuth2ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManagerBean;
@Override
public void configure(HttpSecurity http) throws Exception {
// @formatter:off
final String[] userEndpoints = {
"/v1/api/airline"
};
final String[] adminEndpoints = {
"/v1/api/jobs**"
};
http
.requestMatchers()
.antMatchers(userEndpoints)
.antMatchers(adminEndpoints)
.antMatchers("/secure/**")
.and()
.authorizeRequests()
.antMatchers("/secure/**").authenticated()
.antMatchers(userEndpoints).hasRole("USER")
.antMatchers(adminEndpoints).hasRole("ADMIN");
// @formatter:on
http.addFilterBefore(new BasicAuthenticationFilter(authenticationManagerBean),
UsernamePasswordAuthenticationFilter.class);
}
}
答案 3 :(得分:1)
我认为不可能同时进行身份验证。您可以使用基本身份验证和oauth2身份验证,但适用于不同的端点。正如您所做的那样,第一个配置将克服第二个配置,在这种情况下,将使用http basic。
答案 4 :(得分:1)
解决方案@ kca2ply提供的效果非常好。我注意到浏览器没有发出挑战,所以我稍微调整了一下代码:
@Configuration
@Order(2)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// @formatter:off
http.anonymous().disable()
.requestMatcher(request -> {
String auth = request.getHeader(HttpHeaders.AUTHORIZATION);
return (auth != null && auth.startsWith("Basic"));
})
.antMatcher("/**")
.authorizeRequests().anyRequest().authenticated()
.and()
.httpBasic();
// @formatter:on
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
}
同时使用requestMatcher()
和antMatcher()
使事情完美无缺。如果没有提供,浏览器和HTTP客户端现在将首先挑战基本信用。如果未提供凭据,则会转到OAuth2。
答案 5 :(得分:0)
无法为您提供完整的示例,但这里有一些提示:
粗略地说,spring auth只是从请求(标头)中提取auth数据的请求过滤器和为该auth提供身份验证对象的身份验证管理器的组合。
因此,要在同一个网址上获取基本和oauth,您需要在过滤器链BasicAuthenticationFilter和OAuth2AuthenticationProcessingFilter中安装2个过滤器。
我认为问题在于,配置适配器适用于更简单的配置,因为它们往往会相互覆盖。所以作为第一步尝试移动
.httpBasic();
致电ResourceServerConfiguration
请注意,您还需要提供2个不同的auth管理器:一个用于基本身份验证,一个用于oauth
答案 6 :(得分:0)
为什么不反过来呢?如果没有附加令牌,只需绕过资源服务器,然后回退到正常的安全过滤器链。这就是资源服务器过滤器停止运行的方式。
@Configuration
@EnableResourceServer
class ResourceServerConfig : ResourceServerConfigurerAdapter() {
@Throws(Exception::class)
override fun configure(resources: ResourceServerSecurityConfigurer) {
resources.resourceId("aaa")
}
/**
* Resources exposed via oauth. As we are providing also local user interface they are also accessible from within.
*/
@Throws(Exception::class)
override fun configure(http: HttpSecurity) {
http.requestMatcher(BearerAuthorizationHeaderMatcher())
.authorizeRequests()
.anyRequest()
.authenticated()
}
private class BearerAuthorizationHeaderMatcher : RequestMatcher {
override fun matches(request: HttpServletRequest): Boolean {
val auth = request.getHeader("Authorization")
return auth != null && auth.startsWith("Bearer")
}
}
}
答案 7 :(得分:0)
如果有人想让它与Spring WebFlux一起使用,则确定请求是否得到处理的方法称为“ securityMatcher”,而不是“ requestMatcher”。
即
fun configureBasicAuth(http: ServerHttpSecurity): SecurityWebFilterChain {
return http
.securityMatcher(BasicAuthServerWebExchangeMatcher())
.authorizeExchange()
...