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>
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\"}";
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With