Spring Security - REST API和其他URL的单独配置

Ale*_*lex 5 spring spring-mvc spring-security

我有两组URL - 一组是REST API,第二组是 - 非常普通的网站.我想为REST API应用不同的安全规则,以便偶尔调用REST API的用户/脚本将使用401代码(基本身份验证可以正常)或仅403来应答.

所以我想允许访问REST API:

  • 已登录的用户(对于调用REST API的站点页面上的javascript,因此共享同一会话).
  • 一些在WWW-Authenticate标头中使用基本身份验证凭据调用REST API的脚本.

目前我正在试图弄清楚什么配置会让春天"理解"我想要的东西.我想出了以下配置:

<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>
Run Code Online (Sandbox Code Playgroud)

不幸的是,不仅基本身份验证不适用于此配置,但匿名用户发出的请求没有403响应 - 应用程序回复重定向302找到我想要禁止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>
Run Code Online (Sandbox Code Playgroud)

但这也不起作用.

总结一下我想要得到的东西:

  • 允许授权用户访问REST API(授权工具应将cookie中的用户会话记入帐户).
  • 允许脚本访问REST API,如果它指定了正确的身份验证令牌并且未在cookie中指定会话.

UPDATE

在深入了解Spring Security内部之后,我找到了决定是否拒绝某些请求的代码.这是一个所谓的"访问决策选民",基本上所有这些都适用于某些请求,如果链中的一个访问决策选民投票拒绝访问某些资源,则该请求最终被拒绝.

因此,可以通过引入以下列方式运行的特殊访问决策选举来解决原始问题:它尝试从会话中提取相关角色(如果请求中存在任何存在)并进入具有此角色的授权步骤; 如果没有会话,它会尝试根据WWW-Authenticate标头中的凭据对用户进行身份验证,然后使用与给定用户关联的角色进行授权步骤.

Ale*_*lex 3

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

我找到了一个简单的解决方法。我只是将一个休息控制器映射到两组不同的 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>
Run Code Online (Sandbox Code Playgroud)

它看起来是解决该问题的好方法,因为将来您可能希望使用特定于用户或脚本用例的唯一 URL 来扩展“有状态”用户或“无状态”脚本 REST API。

就我而言,当 UX 更改需要 REST API 提供一些旨在实现特定 UI 场景的新方法,并且某些脚本场景可能需要仅添加一些 URL 以支持某些客户端脚本到服务器交互时,可能会发生这种情况。