最近又看起了 springSecurity
,想起了之前遇到的一个关于页面标签的诡异问题: sec:authorize="hasRole('ADMIN')"
可以正确判断,从而正确控制相应的内容显示不显示,但是到了用 sec:authorize-url="/test"
的时候,就不受控制了。对应的链接无论当前用户有没有权限,相应的内容都会显示在页面上。经过了某度上你抄我我抄你,千篇一律的错误答案之后,翻开了官方文档,终于找到了真正的原因。
原文地址: http://blog.prcode.org/2017/11/why-sec-authorize-url-not-work/
在我们用 springSecurity
实现了相关权限的处理后,实际 sec:authorize-url
判断链接是否有权限所使用的权限拦截器并不是我们自定义的 CustomerFilterSecurityInterceptor
,而是系统默认指定的 FilterSecurityInterceptor
,所以每次都会显示有权限。
对于 url
的权限判断,会经过一个默认处理类: DefaultWebInvocationPrivilegeEvaluator
,调用默认的 securityInterceptor
做权限判断,如果判断通过则放行,如果判断不通过则页面不显示。所以关键点就在于这个拦截器的指定。
如果项目配置使用的是传统的 xml
配置的 bean
,则在配置文件中增加一个 WebInvocationPrivilegeEvaluator
的配置即可。
//customerFilterSecurityInterceptor 是我们自己实现权限拦截所定义的一个bean <bean id="customerWebInvocationPrivilegeEvaluator" class="org.springframework.security.web.access.DefaultWebInvocationPrivilegeEvaluator"> <constructor-arg name="securityInterceptor" ref="customerFilterSecurityInterceptor"/> </bean>
如果项目使用的 springBoot
,配置的话就超简单了,如下:
@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
<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
的源码。
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
实现对角色及资源的控制不在本文的讨论范围内。 原文地址: http://blog.prcode.org/2017/11/why-sec-authorize-url-not-work/