spring - Spring Security 自定义身份验证过滤器和授权
问题描述
我已经实现了一个自定义身份验证过滤器,效果很好。在设置会话并将身份验证对象添加到我的安全上下文后,我使用外部身份提供程序并重定向到我最初请求的 URL。
安全配置
@EnableWebSecurity(debug = true)
@Configuration
class SecurityConfig extends WebSecurityConfigurerAdapter {
// this is needed to pass the authentication manager into our custom security filter
@Bean
@Override
AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean()
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
//.antMatchers("/admin/test").hasRole("METADATA_CURATORZ")
.antMatchers("/**").permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(new CustomSecurityFilter(authenticationManagerBean()), UsernamePasswordAuthenticationFilter.class)
}
}
过滤器逻辑
现在,我的自定义过滤器(一旦确认身份)只是硬编码一个角色:
SimpleGrantedAuthority myrole = new SimpleGrantedAuthority("METADATA_CURATORZ")
return new PreAuthenticatedAuthenticationToken(securityUser, null, [myrole])
然后将该身份验证对象(上面返回)添加到我的 SecurityContext 中,然后重定向到所需的端点:
SecurityContextHolder.getContext().setAuthentication(authentication)
控制器端点
@RequestMapping(path = '/admin/test', method = GET, produces = 'text/plain')
String test(HttpServletRequest request) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication()
String roles = auth.getAuthorities()
return "roles: ${roles}"
}
然后,此端点在浏览器中产生以下响应:
“角色:[METADATA_CURATORZ]”
伟大的。因此,我的身份验证和向我的用户应用角色的效果很好。
现在,如果我从安全配置中取消注释这一行:
//.antMatchers("/admin/test").hasRole("METADATA_CURATORZ")
我无法再访问该资源并获得 403 - 即使我们已经证明角色已设置。
这对我来说似乎完全荒谬和破碎,但我不是 Spring Security 专家。
我可能错过了一些非常简单的东西。有任何想法吗?
我有一些问题:
- 我的自定义过滤器是否需要放置在特定的内置过滤器之前,以确保在执行该过滤器之后发生授权步骤?
- 在请求周期中何时进行 antMatcher/hasRole 检查?
- 我是否需要更改我在安全配置链中调用的顺序,以及我应该如何理解我当前编写的配置?它显然没有做我认为应该做的事情。
解决方案
我的自定义过滤器是否需要放置在特定的内置过滤器之前,以确保在执行该过滤器之后发生授权步骤?
你的过滤器必须在之前FilterSecurityInterceptor
,因为那是授权和认证发生的地方。此过滤器是最后被调用的过滤器之一。
现在至于过滤器的最佳位置可能在哪里,这真的取决于。例如,您真的希望您的过滤器出现在前面,因为如果没有,未经身份验证的用户将始终在调用您的过滤器时AnonymousAuthenticationFilter
被“身份验证” 。AnonymousAuthenticationToken
您可以在 中查看过滤器的默认顺序FilterComparator
。这AbstractPreAuthenticatedProcessingFilter
几乎与您正在做的事情相对应 - 它按照过滤器的顺序放置让您了解可以放置您的过滤器的位置。无论如何,您的过滤器顺序应该没有问题。
在请求周期中何时进行 antMatcher/hasRole 检查?
所有这些都发生在FilterSecurityInterceptor
,更准确地说,发生在其父级中AbstractSecurityInterceptor
:
protected InterceptorStatusToken beforeInvocation(Object object) {
Collection<ConfigAttribute> attributes = this.obtainSecurityMetadataSource()
.getAttributes(object);
if (attributes == null || attributes.isEmpty()) {
...
}
...
Authentication authenticated = authenticateIfRequired();
// Attempt authorization
try {
this.accessDecisionManager.decide(authenticated, object, attributes);
}
catch (AccessDeniedException accessDeniedException) {
...
throw accessDeniedException;
}
额外信息:
本质上,FilterSecurityInterceptor
有一个ExpressionBasedFilterInvocationSecurityMetadataSource
包含一个Map<RequestMatcher, Collection<ConfigAttribute>>
。在运行时,检查您的请求Map
以查看是否有任何RequestMatcher
键匹配。如果是,则将 aCollection<ConfigAttribute>
传递给AccessDecisionManager
,最终将授予或拒绝访问权限。默认AccessDecisionManager
是AffirmativeBased
并包含处理集合的对象(通常是 a WebExpressionVoter
),ConfigAttribute
并通过反射调用SpelExpression
与您相对应"hasRole('METADATA_CURATORZ')"
的SecurityExpressionRoot
对象,该对象是用您的Authentication
.
我是否需要更改我在安全配置链中调用的顺序,以及我应该如何理解我当前编写的配置?它显然没有做我认为应该做的事情。
不,您的过滤器应该没有任何问题。顺便说一句,除了您的configure(HttpSecurity http)
方法之外,WebSecurityConfigurerAdapter
您扩展的方法还有一些默认值:
http
.csrf().and()
.addFilter(new WebAsyncManagerIntegrationFilter())
.exceptionHandling().and()
.headers().and()
.sessionManagement().and()
.securityContext().and()
.requestCache().and()
.anonymous().and()
.servletApi().and()
.apply(new DefaultLoginPageConfigurer<>()).and()
.logout();
HttpSecurity
如果您想确切了解它们的作用以及它们添加的过滤器,您可以查看一下。
问题
当您执行以下操作时:
.authorizeRequests()
.antMatchers("/admin/test").hasRole("METADATA_CURATORZ")
...搜索的角色是"ROLE_METADATA_CURATORZ"
。为什么?
ExpressionUrlAuthorizationConfigurer
的静态hasRole(String role)
方法结束处理"METADATA_CURATORZ"
:
if (role.startsWith("ROLE_")) {
throw new IllegalArgumentException(
"role should not start with 'ROLE_' since it is automatically inserted. Got '"
+ role + "'");
}
return "hasRole('ROLE_" + role + "')";
}
所以你的授权表达式变成"hasRole('ROLE_METADATA_CURATORZ'"
了,这最终调用了hasRole('ROLE_METADATA_CURATORZ')
on方法,该方法又在's 的权限SecurityExpressionRoot
中搜索角色。ROLE_METADATA_CURATORZ
Authentication
解决方案
改变
SimpleGrantedAuthority myrole = new SimpleGrantedAuthority("METADATA_CURATORZ");
至:
SimpleGrantedAuthority myrole = new SimpleGrantedAuthority("ROLE_METADATA_CURATORZ");
推荐阅读
- sql - 发生错误时从选择语句继续插入
- arrays - 通过变量间接访问 bash 关联数组
- python-3.x - 如何更改音频的标题?
- xslt - 使用 JEuclid 生成 PDF(XSL-FO) 中的公式
- reactjs - 为什么我们从历史中导入 createBrowserHistory?以及为什么我们将它传递给路由器
有一个名为 的文件
"createBrowserHistory.d.ts"
,那个文件有什么用?下面的代码没有错误,但是当我尝试运行它时,它会显示空白?为什么这样?<
- cluster-analysis - 聚类方法会解决这个嘈杂的匹配问题吗?
- javascript - 检测与图像和画布上的对象的碰撞
- php - 给定文本存储为降价,如何使用 php 进行文本预览
- amazon-web-services - VPC 外部的 AWS Lambda 和 RDS
- python - 在pyqt4 python中的QTableWidget中存储列表值