使用 Spring Security 'hasPermission()' 在未经授权的 REST 服务请求上返回 JSON

Posted

技术标签:

【中文标题】使用 Spring Security \'hasPermission()\' 在未经授权的 REST 服务请求上返回 JSON【英文标题】:Return JSON on unauthorized REST service request using Spring Security 'hasPermission()'使用 Spring Security 'hasPermission()' 在未经授权的 REST 服务请求上返回 JSON 【发布时间】:2014-06-26 19:11:52 【问题描述】:

我正在使用 Spring 安全性实现方法级别限制/授权(在 REST 服务 上)。 我使用了 spring 表达式语言并实现了自定义表达式评估器。

它对我来说很好用。但是,如果未经授权的用户尝试访问该服务,则会使用登录页面进行响应。由于我的应用程序仅基于 REST,因此我只想为所有请求返回 JSON 数据。

如何让它返回 JSON 而不是登录页面?(例如:status : Denied

这里是代码片段:

自定义评估器

public boolean hasPermission(Authentication authentication, Object userId, Object permissionId) 
        List<String> permList = new MyDAO().getPermissionsForUser((String) userId);
        if(permList.contains(((String) permissionId)))
            return true;
        else
            return false;
        
    

服务

@PreAuthorize("hasPermission(#userId, '3')")
    public String testAuthorization(Object obj, String userId)

        System.out.println("Has access to the service method....");

        return  "success";
    

控制器

public @ResponseBody String testAuthorization(Object o,@RequestParam("userId") String userId)
        System.out.println("User ID = "+userId);
        String abc = service.testAuthorization(o,userId);
        return "\"status\":\"success\"";
    

spring-security.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:security="http://www.springframework.org/schema/security"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
            http://www.springframework.org/schema/security 
            http://www.springframework.org/schema/security/spring-security-3.0.xsd">


    <security:global-method-security secured-annotations="enabled" pre-post-annotations="enabled">
        <security:expression-handler ref="expressionHandler"/>
    </security:global-method-security>

    <!-- This is where we configure Spring-Security  -->
    <security:http auto-config="true" use-expressions="true" access-denied-page="/auth/auth/denied" >
        <security:intercept-url pattern="/auth/auth/login" access="permitAll"/>
        <security:intercept-url pattern="/auth/main/admin" access="hasRole('ROLE_ADMIN')"/>
        <security:intercept-url pattern="/auth/main/common" access="hasRole('ROLE_USER')"/>


    </security:http>

    <security:authentication-manager>
        <security:authentication-provider user-service-ref="customUserDetailsService">
            <security:password-encoder ref="passwordEncoder"/>
        </security:authentication-provider>
    </security:authentication-manager>

    <!-- Use a Md5 encoder since the user's passwords are stored as Md5 in the database -->
    <bean class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" id="passwordEncoder"/>

    <!-- A custom service where Spring will retrieve users and their corresponding access levels  -->
    <bean id="customUserDetailsService" class="com.cjl.security.service.CustomUserDetailsService"/>

    <bean id="expressionHandler" class="org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler">
        <property name="permissionEvaluator" ref="permissionEvaluator"/>
    </bean>

    <bean id="permissionEvaluator" class="com.cjl.security.evaluators.MethodPermissionEvaluator"/>


</beans>

【问题讨论】:

【参考方案1】:

正在发生的事情是一个 AccessDeniedException 被抛出,因此您想要配置您的系统以拦截该异常并返回 JSON。

您可以在您的控制器中设置一个@ExceptionHandler 方法来捕获AccessDeniedException。但是,您可能希望在所有控制器中执行相同的操作,因此如果您使用 Spring 3.2,则可以在单独的“建议”类上使用 @ControllerAdvice 注释,然后在其中包含 @ExceptionHandler 方法。

@ControllerAdvice 
public class ExceptionControllerAdvice 

    @ExceptionHandler(AccessDeniedException.class)
    @ResponseBody
    public String exception(AccessDeniedException e) 
        return "\"status\":\"access denied\"";
     

【讨论】:

我使用的是 spring-security 版本 3.0.5。我将 @ExceptionHandler(AccessDeniedException.class) 添加到我的控制器中。仍然获得相同的登录页面。 对不起,我的错误。它现在正在工作。在 3.0.5 版本中,如果我必须在另一个控制器中处理相同的异常,该怎么办?复制异常处理是唯一的方法? 您可以复制处理程序,也可以创建一个 abstractController 来扩展所有其他控制器。您还可以创建一个方面 itd 来声明所有控制器的处理程序,尽管我承认我没有尝试过。 谢谢。 :) 我会努力的 这是我的错。我尝试了您的解决方案,但请求重定向到 /error 页面,它不起作用。最后我发现org.springframework.web.filter期间的异常抛出不会被@ ControllerAdvice 捕获

以上是关于使用 Spring Security 'hasPermission()' 在未经授权的 REST 服务请求上返回 JSON的主要内容,如果未能解决你的问题,请参考以下文章

在使用 Oauth、SAML 和 spring-security 的多租户的情况下从 spring-security.xml 中获取错误

Spring security:Spring security 如何在 SessionRegistry 中注册新会话?

未调用 Spring Security j_spring_security_check

使用 spring-session 和 spring-cloud-security 时,OAuth2ClientContext (spring-security-oauth2) 不会保留在 Redis

如何使用 Spring-Security 3 和 Hibernate 4 将 spring security xml 配置 hibernate 转换为 java config

Spring-Security:升级到 Spring-Security 4.1 后,用户名发送为空以进行登录