Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to return 401 for REST API call in Spring Security?

Following is my sample Spring Security configuration.

I want all /api to return HTTP 401 code instead of redirecting 302 to login page.

Also I want to keep the redirect feature for old web pages.

<security:http auto-config='true' use-expressions="true" >
    <security:intercept-url pattern="/api*" access="hasRole('USER')" />
    <security:intercept-url pattern="/oldweb*" access="hasRole('USER')" />

    <security:form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?login_error=1" default-target-url="/home"/>    
</security:http>
like image 874
mohan Avatar asked Feb 21 '17 04:02

mohan


People also ask

How are REST services secured using Spring Security?

The following Spring security setup works as following: The user logs in with a POST request containing his username and password, The server returns a temporary / permanent authentication token, The user sends the token within each HTTP request via an HTTP header Authorization: Bearer TOKEN .


2 Answers

I came to more simpler solution. In Spring Boot and Java config you just have to register additional entry point in addition to default one. And because all your rest-services resides in "/api" name space, you could use AntPathRequestMatcher("/api/**") to match necessary requests.

So, final solution is:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
    http.exceptionHandling()
                    //Actually Spring already configures default AuthenticationEntryPoint - LoginUrlAuthenticationEntryPoint
                    //This one is REST-specific addition to default one, that is based on PathRequest
                    .defaultAuthenticationEntryPointFor(getRestAuthenticationEntryPoint(), new AntPathRequestMatcher("/api/**"));
    }

    private AuthenticationEntryPoint getRestAuthenticationEntryPoint() {
        return new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED);
    }
}
like image 188
Oleksandr_DJ Avatar answered Sep 22 '22 21:09

Oleksandr_DJ


You need to have a custom authentication entry point.

public class CustomEntryPoint extends LoginUrlAuthenticationEntryPoint {

    private static final String XML_HTTP_REQUEST = "XMLHttpRequest";
    private static final String X_REQUESTED_WITH = "X-Requested-With";

    public CustomEntryPoint(String loginFormUrl) {
        super(loginFormUrl);
    }

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception)
            throws IOException, ServletException {
        if (XML_HTTP_REQUEST.equals(request.getHeader(X_REQUESTED_WITH))) {
            response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
        } else {
            super.commence(request, response, exception);
        }
    }    
}

Finally change your config to this:

<security:http auto-config='true' use-expressions="true" entry-point-ref="customEntryPoint">
    <security:intercept-url pattern="/api*" access="hasRole('USER')" />
    <security:intercept-url pattern="/oldweb*" access="hasRole('USER')" />

    <security:form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?login_error=1" default-target-url="/home"/>

    <beans:bean id="customEntryPoint" class="CustomEntryPoint">
        <beans:constructor-arg value="/login"/>
    </beans:bean>    
</security:http>
like image 32
Monzurul Shimul Avatar answered Sep 20 '22 21:09

Monzurul Shimul