Spring Security——单独配置REST API和其他url



我有两组url -一组是REST API,另一组是非常普通的网站。我想为REST API应用不同的安全规则,以便偶尔调用REST API的用户/脚本将得到401代码(基本授权就可以了)或403代码的回答。

所以我想允许访问REST API:
  • 已登录的用户(对于调用REST API的站点页面上的javascript,因此共享同一会话)。
  • 一些调用REST API的脚本,在WWW-Authenticate头中使用基本的认证凭证。

此刻,我正试图弄清楚什么配置将使spring"理解"我想要的。我提出了以下配置:

<beans:beans xmlns="http://www.springframework.org/schema/security"
             xmlns:beans="http://www.springframework.org/schema/beans"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="
                http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
                http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">
    <http pattern="/" security="none" />
    <http pattern="/static/**" security="none" />
    <!-- REST API -->
    <http pattern="/rest/*" use-expressions="true">
        <http-basic />
        <intercept-url pattern="/*" access="isAuthenticated()" />
    </http>
    <!-- Site -->
    <http access-denied-page="/WEB-INF/views/errors/403.jsp" use-expressions="true">
        <intercept-url pattern="/index.html" access="hasRole('ROLE_USER') or hasRole('ROLE_ANONYMOUS')" />
        <intercept-url pattern="/login.html" access="hasRole('ROLE_USER') or hasRole('ROLE_ANONYMOUS')" />
        <intercept-url pattern="/admin/*" access="hasRole('ROLE_ADMIN')" />
        <intercept-url pattern="/**" access="hasRole('ROLE_USER')" />
        <form-login login-page="/login.html"
                    default-target-url="/index.html"
                    authentication-failure-url="/login.html?error=1" />
        <logout logout-url="/logout.do" logout-success-url="/index.html" />
        <anonymous username="guest" granted-authority="ROLE_ANONYMOUS" />
        <remember-me />
    </http>
    <authentication-manager>
        <authentication-provider>
            <user-service>
                <user name="admin" password="2" authorities="ROLE_ADMIN,ROLE_USER" />
                <user name="alex" password="1" authorities="ROLE_USER" />
            </user-service>
        </authentication-provider>
    </authentication-manager>
</beans:beans>

不幸的是,不仅基本身份验证不适用于此配置,而且对于匿名用户发出的请求没有403响应-应用程序回答重定向302 Found,我想禁止REST API url。

我尝试为REST API添加自定义入口点:

<beans:bean id="ep403" class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint"/>

<!-- REST API -->
<http pattern="/rest/*" entry-point-ref="ep403" use-expressions="true">
    <intercept-url pattern="/*" access="hasRole('ROLE_USER')" />
    <http-basic />
</http>

但这也不工作

总结一下我想要得到的:

  • 允许授权用户访问REST API(授权工具应该考虑cookie中的用户会话)。
  • 允许脚本访问REST API,如果它指定了正确的身份验证令牌,而没有在cookie中指定会话。

在深入研究Spring Security内部之后,我发现了决定是否拒绝某些请求的代码。这就是所谓的"访问决策投票人",基本上所有的访问决策投票人都适用于特定的请求,如果链中的一个访问决策投票人投票拒绝访问某些资源,则请求最终被拒绝。

因此,最初的问题可以通过引入特殊的访问决策投票器来解决,它的行为如下:它尝试从会话中提取相关的角色(如果请求中存在),并使用此角色进行授权步骤;如果没有会话,它尝试根据WWW-Authenticate头中的凭据对用户进行身份验证,然后使用与给定用户关联的角色进行授权步骤。

转发作为bluish@建议的解决方案:)希望这将对某人有所帮助。

我找到了一个简单的解决方法。我只是将一个rest控制器映射到两个不同的url集,并将每个url集分配给spring安全配置中不同的授权处理程序:

<!-- Defines custom security policy for Stateful REST API -->
<beans:bean id="nonRedirectingAccessDeniedHandler" class="org.springframework.security.web.access.AccessDeniedHandlerImpl"/>
<beans:bean id="forbiddenEntryPoint" class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint"/>
<!-- Stateful REST API -->
<http pattern="/rest/stateful/**" use-expressions="true" entry-point-ref="forbiddenEntryPoint">
    <access-denied-handler ref="nonRedirectingAccessDeniedHandler"/>
    <intercept-url pattern="/rest/stateful/**" access="isAuthenticated()" />
</http>
<!-- Stateless REST API -->
<http pattern="/rest/stateless/**" use-expressions="true" create-session="stateless">
    <http-basic/>
    <intercept-url pattern="/rest/stateless/**" access="isAuthenticated()" />
</http>

这看起来是解决这个问题的好方法,因为将来你可能想扩展"有状态"-用户或"无状态"-脚本REST API,使用特定于用户或脚本用例的唯一url。

在我的例子中,当UX变化需要REST API提供一些设计用于实现特定UI场景的新方法,而一些脚本场景可能需要添加一些url来单独支持某些客户端脚本到服务器的交互时,可能会发生这种情况。

你可以使用拦截器来检查url是否有/rest/模式

<mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/rest/**"/>
            <bean class="com.your.class.name.Interceptor></bean>
        </mvc:interceptor>
</mvc:interceptors>

在XML的标题部分添加下面一行,即

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:mvc="http://www.springframework.org/schema/mvc"

现在在Interceptor类检查你需要什么,这个类应该实现Spring HandlerInterceptor

public class Interceptor implements HandlerInterceptor{
    @Override  
    public boolean preHandle(HttpServletRequest request,  
                             HttpServletResponse response,  
                             Object handler) throws Exception {
       //do what you need to check when the request arrives
       //do authentications here
       //return true if success
       //else false
    }
    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response, 
                           Object handler,
                           ModelAndView modelAndView) throws Exception {
    }
    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response, 
                                Object handler, 
                                Exception ex) throws Exception {    
    }
}

阅读这篇文章了解更多关于这些方法的细节。也可以看到

尝试将pattern="/rest/*"更改为pattern="/rest/**"pattern="/*"更改为pattern="/**"的REST API:

<http pattern="/rest/**" use-expressions="true">
    <intercept-url pattern="/**" access="isAuthenticated()" />
    <http-basic />
</http>

相关内容

最新更新