0%

springSecurity 中为什么 sec:authorize-url 不起作用

​ 最近又看起了 springSecurity ,想起了之前遇到的一个关于页面标签的诡异问题:sec:authorize="hasRole('ADMIN')" 可以正确判断,从而正确控制相应的内容显示不显示,但是到了用 sec:authorize-url="/test" 的时候,就不受控制了。对应的链接无论当前用户有没有权限,相应的内容都会显示在页面上。经过了某度上你抄我我抄你,千篇一律的错误答案之后,翻开了官方文档,终于找到了真正的原因。

原因及解决方案

原因

​ 在我们用 springSecurity 实现了相关权限的处理后,实际 sec:authorize-url 判断链接是否有权限所使用的权限拦截器并不是我们自定义的 CustomerFilterSecurityInterceptor ,而是系统默认指定的 FilterSecurityInterceptor ,所以每次都会显示有权限。

​ 对于 url 的权限判断,会经过一个默认处理类: DefaultWebInvocationPrivilegeEvaluator ,调用默认的 securityInterceptor 做权限判断,如果判断通过则放行,如果判断不通过则页面不显示。所以关键点就在于这个拦截器的指定。

解决方案

​ 如果项目配置使用的是传统的 xml 配置的 bean ,则在配置文件中增加一个 WebInvocationPrivilegeEvaluator 的配置即可。

1
2
3
4
//customerFilterSecurityInterceptor 是我们自己实现权限拦截所定义的一个bean
<bean id="customerWebInvocationPrivilegeEvaluator" class="org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator">
<constructor-arg name="securityInterceptor" ref="customerFilterSecurityInterceptor"/>
</bean>

​ 如果项目使用的 springBoot ,配置的话就超简单了,如下:

1
2
3
4
5
6
7
8
9
10
11
@Configuration
@EnableWebSecurity
public class WebOOSSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 其余部分省略
*/
@Override
public void configure(WebSecurity web) throws Exception {
web.privilegeEvaluator(new DefaultWebInvocationPrivilegeEvaluator(customerFilterSecurityInterceptor()));
}
}

注:本部分假定读者已解决 springSecurity 进行鉴权部分。

解决过程

查看官方文档

在官方文档中,关于 sec:authorize-url 的部分是这样描述的

文档地址:https://docs.spring.io/spring-security/site/docs/4.2.3.RELEASE/reference/htmlsingle/#declaring-the-taglib

1
2
3
<sec:authorize url="/admin">
This content will only be visible to users who are authorized to send requests to the "/admin" URL.
</sec:authorize>

To use this tag there must also be an instance of WebInvocationPrivilegeEvaluator in your application context. If you are using the namespace, one will automatically be registered. This is an instance of DefaultWebInvocationPrivilegeEvaluator, which creates a dummy web request for the supplied URL and invokes the security interceptor to see whether the request would succeed or fail. This allows you to delegate to the access-control setup you defined using intercept-url declarations within the <http> namespace configuration and saves having to duplicate the information (such as the required roles) within your JSPs. This approach can also be combined with a method attribute, supplying the HTTP method, for a more specific match.

​ 大致意思是说,想要用这个标签,必须要配置一个 WebInvocationPrivilegeEvaluator 的实例,不然 Spring 就会自动使用默认的 DefaultWebInvocationPrivilegeEvaluator 来做处理。

​ 到这里,意思就已经很明显了,回想一下,自己在做 springSecurity 的时候,是不是只做了权限拦截器,来保证每个请求都得到正确的鉴权,但是并没有做 WebInvocationPrivilegeEvaluator 的配置?

查看源码

​ 在官方文档中,我们已经知道了,需要多加一个类的配置,那么到底该怎么配置呢?我们来查看一下DefaultWebInvocationPrivilegeEvaluator 的源码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class DefaultWebInvocationPrivilegeEvaluator implements WebInvocationPrivilegeEvaluator {
//权限拦截器
private final AbstractSecurityInterceptor securityInterceptor;
//可以指定权限拦截器的构造器
public DefaultWebInvocationPrivilegeEvaluator(AbstractSecurityInterceptor securityInterceptor) {
this.securityInterceptor = securityInterceptor;
}
//是否放行
public boolean isAllowed(String contextPath, String uri, String method, Authentication authentication) {
...
try {
//权限拦截器中的决策器来判断是否通过
securityInterceptor.getAccessDecisionManager().decide(authentication, fi, attrs);
}
catch (AccessDeniedException unauthorized) {
...
return false;
}
return true;
}
}

​ 由于我们未指定声明权限计算器,默认的权限判断,使用的是默认的权限拦截器。但是,该类刚好提供了一个可以传入权限拦截器的构造器,所以,我们可以将之前做权限校验时写好的权限拦截器做为参数传入。这样就可以做到,如果某链接有访问权限,页面则显示对应的链接。若无,则不显示。

总结

  • 如果 springSecurity 的标签一个都不起作用,请检查项目是否引入了对应的处理标签的 jar 包。如,使用 thymeleaf 则引入 thymeleaf-extras-springsecurity4 即可。
  • 如果其它的标签都起作用,就单单判断是否有某链接权限的 sec:authorize-url 不起作用,是因为项目少配置了 WebInvocationPrivilegeEvaluator ,配置即可。
  • springSecurity 实现对角色及资源的控制不在本文的讨论范围内。