[英]<Spring Security> Having OAuth2 and basic authentication in the same application
[英]Spring security with Oauth2 or Http-Basic authentication for the same resource
我正在尝试使用受 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 尝试两者?
任何帮助将不胜感激。
我设法根据 Michael Ressler 的回答的提示完成了这项工作,但做了一些调整。
我的目标是在相同的资源端点上允许 Basic Auth 和 Oauth,例如 /leafcase/123。 由于 filterChains 的排序,我被困了很长一段时间(可以在 FilterChainProxy.filterChains 中检查); 默认顺序如下:
由于资源服务器的过滤器链排名高于 WebSecurityConfigurerAdapter 配置的过滤器链,并且前者几乎匹配每个资源端点,因此 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:Basic 资源请求,因此任何带有 Authorization:Bearer 的请求都会失败,然后由资源服务器的 filterChain 处理(即 Oauth 的过滤器启动)。 此外,它的排名低于 AuthenticationServer(如果 AuthenticationServer 在同一项目上启用),因此它不会阻止 AuthenticaitonServer 的过滤器链处理对 /oauth/token 等的请求。
这可能与您要查找的内容相近:
@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。 如果客户端是执行者,那么我们需要这个过滤器介入并确保请求得到正确的身份验证。
为什么不反过来呢? 如果没有附加令牌,只需绕过资源服务器,然后回退到正常的安全过滤器链。 这是资源服务器过滤器停止的方式。
@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")
}
}
}
@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。
您可以向安全过滤器链添加 BasicAuthenticationFilter 以在受保护资源上获得 OAuth2 或基本身份验证安全性。 示例配置如下...
@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);
}
}
无法为您提供完整的示例,但这里有一个挖掘提示:
粗略地说,spring auth 只是从请求(标头)中提取身份验证数据的请求过滤器和为该身份验证提供身份验证对象的身份验证管理器的组合。
因此,要在同一 url 中获取基本和 oauth,您需要在过滤器链 BasicAuthenticationFilter 和 OAuth2AuthenticationProcessingFilter 中安装 2 个过滤器。
我认为问题在于,ConfiguringAdapters 适用于更简单的 confs,因为它们往往会相互覆盖。 所以作为第一步尝试移动
.httpBasic();
调用ResourceServerConfiguration
请注意,您还需要提供 2 个不同的身份验证管理器:一个用于基本身份验证,一个用于 oauth
我相信不可能同时进行两种身份验证。 您可以进行基本身份验证和 oauth2 身份验证,但适用于不同的端点。 按照您的方式,第一个配置将克服第二个配置,在这种情况下,将使用 http basic。
如果有人试图使用 Spring WebFlux 进行此操作,则确定请求是否被处理的方法称为“securityMatcher”,而不是“requestMatcher”。
IE
fun configureBasicAuth(http: ServerHttpSecurity): SecurityWebFilterChain {
return http
.securityMatcher(BasicAuthServerWebExchangeMatcher())
.authorizeExchange()
...
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.