Question

I am implementing method level restriction/authorization (on REST services) using Spring security.
I have used the spring expression language and implemented custom Expression Evaluator.

It is working fine for me. However, if an unauthorized user tries to access the service, it is responding with a login page. Since my application is REST based only, I want to return only JSON data for all the requests.

How do I make it return JSON instead of the login page?(eg: {status : Denied})

Here is the code Snippet:

CustomEvaluator

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;
        }
    }

Service

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

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

        return  "success";
    }

Controller

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>
Was it helpful?

Solution

What is happening is an AccessDeniedException is being thrown, so you want to configure your system to intercept that exception and instead return JSON.

You can set up an @ExceptionHandler method within your controller which catches the AccessDeniedException. However, you probably want to do the same thing in all your controllers, so if you are using Spring 3.2, you can use @ControllerAdvice annotation on a separate 'advice' class and then include the @ExceptionHandler method in there.

@ControllerAdvice 
public class ExceptionControllerAdvice {

    @ExceptionHandler(AccessDeniedException.class)
    @ResponseBody
    public String exception(AccessDeniedException e) {
        return "{\"status\":\"access denied\"}";
    } 
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top