6

Spring Security has the assumption of Authentication is a Principal.

public interface Authentication extends Principal, Serializable {}

HttpServletRequest has the method of getUserPrincipal which is responsible for accessing principal object.

Let's consider this case:

public interface RealPrincipal extends Principal {
   public Integer getId();
}

Common Module A has Real Principal interface and implementation.

Module A uses Common Module A, Servlet Api and does not depend on Spring Security:

Module B uses Common Module A, Servlet Api and configures Spring Security. This module responsible for security and UserDetails implementation.

Web A uses Module A and Module B.

In order to use request methods, I am ending up with such an implementation:

public ModelAndView someRequestHandler(Principal principal) {
   User activeUser = (User) ((Authentication) principal).getPrincipal();
   ...
}

This is forcing me to have dependency of Spring Security for the Module A and other modules. I believe that a proper servlet api abstraction should not depend on spring security. request.getUserPrincipal should return real principal.

Please explain why org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestWrapper returns

Authentication instead of Real Principal.

Edit: I have added Common Module A to my scenario and updated that Module B is responsible for security.

Cemo
  • 5,370
  • 10
  • 50
  • 82

2 Answers2

5

As Luke stated, Spring Security uses the Authentication for the Principal because it implements Principal. It does not use the Authentication#getPrincipal() because it is not guaranteed to be a Principal (it is an Object). In fact, in most situations Spring Security's Authentication#getPrincipal() returns a User (does not implement Principal), a custom UserDetails provided by users of the framework, or a String.

If you want Spring Security to handle this, you will likely need to implement this logic using an HttpServletRequestWrapper as Luke suggested. For example, you could do the following:

public RealPrincipalFilter extends OncePerRequestFilter {

    public void doFiter(HttpServletRequest request, HttpServletResponse response, FilterChain) {
        chain.doFilter(new RealPrincipalRequestWrapper(request), response);
    }

    private static final class RealPrincipalRequestWrapper 
          extends HttpServletRequestWrapper {
        public Principal getUserPrincipal() {
            Authentication auth = (Authentication) super.getPrincipal();
            return auth == null ? null : (RealPrincipal) auth.getPrincipal()
        }
    }
}

@Configuration
@EnableWebSecurity
public WebSecurityConfig extends WebSecurityConfigurerAdapter {
    public configure(HttpSecurity http) {
        http
            // ... other config ...
            .addFilterAfter(new RealPrincipalFilter(), SecurityContextHolderAwareRequestFilter.class);
    }
    ...
}

Alternatively, take a look at my answer on your other question for options to integrate with Spring MVC - Injecting Custom Principal to Controllers by Spring Security

Community
  • 1
  • 1
Rob Winch
  • 21,440
  • 2
  • 59
  • 76
  • Thanks Rob for you great answers. Now I understood that Spring Security is not only related to Web application but also Standalone applications. It means that User does not need to be a Principal. And In order to comply with Http Api, Authentication had implemented Principal interface. So far so good :) Here is my question: What if Spring Security custom User is already implementing Principal interface? This results in such an implementation which is perfect I think: https://gist.github.com/cemo/6041868 what do you think? – Cemo Jul 19 '13 at 19:51
  • The change you propose would be non-passive so at best it could be include in a 4.x release. I don't think this is a use case that most are needing so I don't think it will get included in 4.x either. This isn't to say that you don't have good reason to do what you are doing, but that as a framework we need to try to aim for the 90% usecases and have hooks for the other cases. – Rob Winch Jul 19 '13 at 21:18
1

The short answer is that Authentication is a Principal so that it can be used in APIs (such as the servlet API method you mention) which require one.

What does this mean in practice? Not a lot. Java's Principal interface has only one method getName, so if you want to do more than render the user's name, you need to know something more about the implementation.

You should probably think about what you mean when you use the phrases "real principal" and "proper servlet api abstraction". How would you expect to implement your someRequestHandler method if the principal was a "real" one, for example?

Shaun the Sheep
  • 22,353
  • 1
  • 72
  • 100
  • Consider RealPrincipal as subclass of principal. It has all necessary methods such as username, email, password etc... I can depend on it. No problem. But depending on Spring Security is not a feasible solution. There is also another problem. Spring MVC is injecting Principal with request.getUserPrincipal. If I declare RealPrincipal as a controller method argument, it can not inject. Because Authentication is not a RealPrincipal but a Principal. This is causing an error. – Cemo Jul 19 '13 at 11:45
  • and please also see my another question http://stackoverflow.com/questions/17741787/injecting-custom-principal-to-controllers-by-spring-security. – Cemo Jul 19 '13 at 11:47
  • What is `RealPrincipal` and where do you expect it to come from? i.e. who provides it and how can you depend on it? – Shaun the Sheep Jul 19 '13 at 11:51
  • I have added RealPrincipal interface. It is coming from another module like servlet-api. And Module B is providing necessary implementation. By the the way this is not a fictional scenario. I have exactly in the same situation as I have written above. – Cemo Jul 19 '13 at 11:58
  • If you want `RealPrincipal` returned from `getUserPrincipal` method, then the best option is probably to add a separate filter of your own, after the spring security filter chain, which uses an `HttpServletRequestWrapper` to customize the method. You could also disable the built-in wrapper. – Shaun the Sheep Jul 19 '13 at 14:54