Search code examples
spring-bootspring-security-oauth2spring-session

How to protect the same resource using both spring-session and spring-security-oauth


I have a requirement to use two kinds of authentication, for web we @EnableRedisHttpSession and for other consumers like mobile we use @EnableAuthorizationServer with @EnableResourceServer.

suppose we try to protect a controller common to both the authentication mechanisms for e.g /api/v1/test

i have hit a roadblock. i am only able to use one kind of authentication scheme if i set @WebSecurityConfigurerAdapter @order(2) and @ResourceServerConfigurerAdapter @order(3) then i can only access the resource via web

and if i set @ResourceServerConfigurerAdapter @order(2) and @WebSecurityConfigurerAdapter @order(3) then only OAuth works.

i am unable to use both the mechanism at the same time.how can we make the two work together, for e.g if the request comes from web use the filter responsible for that and if the request comes from mobile use the appropriate filter. web uses cookies and API Consumers use Authorization : Bearer header.

please help


Solution

  • It's sounds very strange. I suggest you to review how REST API is used and why it should be used by browser users. Better to separate web views and REST API, don't mix it.

    However, answering your question "can I use two kinds of authentication for some URI at once" - yes, you can.
    You need custom RequestMatcher that will decide how to route incoming request.

    So:

    • for "API consumers" - check existence of Authorization header contains "Bearer"
    • for "browser users" - just inverse first rule

    Code example:

    public abstract class AntPathRequestMatcherWrapper implements RequestMatcher {
    
        private AntPathRequestMatcher delegate;
    
        public AntPathRequestMatcherWrapper(String pattern) {
            this.delegate = new AntPathRequestMatcher(pattern);
        }
        
        @Override
        public boolean matches(HttpServletRequest request) {
            if (precondition(request)) {
                return delegate.matches(request);
            }
            return false;
        }
    
        protected abstract boolean precondition(HttpServletRequest request);
    
    }
    
    1. OAuth2 authentication
    @EnableResourceServer
    @Configuration
    public class ResourceServerConfigurer extends ResourceServerConfigurerAdapter {
        @Override
        public void configure(HttpSecurity http) throws Exception {
            http.requestMatcher(new AntPathRequestMatcherWrapper("/api/v1/test") {
                @Override
                protected boolean precondition(HttpServletRequest request) {
                    return String.valueOf(request.getHeader("Authorization")).contains("Bearer");
                }
            }).authorizeRequests().anyRequest().authenticated();
        }
    }
    
    1. Web authentication
    @Configuration
    @EnableWebSecurity
    public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.requestMatcher(new AntPathRequestMatcherWrapper("/api/v1/test") {
                @Override
                protected boolean precondition(HttpServletRequest request) {
                    return !String.valueOf(request.getHeader("Authorization")).contains("Bearer");
                }
            }).authorizeRequests().anyRequest().authenticated();
        }
    }
    

    Using this configuration it's possible to use two different authentication types for one URI /api/v1/test.

    In addition, I highly recommended to read the article about Spring Security architecture by Dave Syer, to understand how does it work:
    https://spring.io/guides/topicals/spring-security-architecture/#_web_security