AuthenticationPrincipal is empty when using EnableWebSecurity

后端 未结 4 841
挽巷
挽巷 2021-01-13 03:25

As of Spring Security doc: 34.1 @EnableWebMvcSecurity states, the @EnableWebMvcSecurity was replaced by @EnableWebSecurity.

But when I try

相关标签:
4条回答
  • 2021-01-13 03:37

    AuthenticationPrincipal is empty.
    But I can get the UserDetails by the traditional way, like this:

    SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    

    Configuring of argument-resolvers helped for me

    <mvc:annotation-driven>
        <mvc:argument-resolvers>
            <bean class="org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver"/>
        </mvc:argument-resolvers>
    </mvc:annotation-driven>
    

    Also you can see 34.2 @AuthenticationPrincipal section

    Once AuthenticationPrincipalArgumentResolver is properly configured, you can be entirely decoupled from Spring Security in your Spring MVC layer.

    How i understand this mean you should add argument resolver manually. Or

    By using Section 34.1, “@EnableWebMvcSecurity” you will automatically have this added to your Spring MVC configuration

    Also related question

    0 讨论(0)
  • 2021-01-13 03:41

    I met the same problem. I had to make my own HandlerMethodArgumentResolver and annotation. The following code have been tested and works

    First create the annotation

    @Target({ ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
    @Retention(RetentionPolicy.RUNTIME)
    public @interface CurrentUser{}
    

    And a simple config

    @Configuration
    @EnableWebMvc
    @ComponentScan(basePackages = "x.x.x")
    public class ApplicationConfiguration extends WebMvcConfigurerAdapter{
    
        @Override
        public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
            argumentResolvers.add(new HandlerMethodArgumentResolver() {
    
                public boolean supportsParameter(MethodParameter parameter) {
                    return findMethodAnnotation(CurrentUser.class, parameter) != null;
                }
    
                public Object resolveArgument(
                        MethodParameter parameter,
                        ModelAndViewContainer mavContainer,
                        NativeWebRequest webRequest,
                        WebDataBinderFactory binderFactory) throws Exception
                {
                    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
                    Object principal = authentication.getPrincipal();
    
                    if(principal != null && !parameter.getParameterType().isAssignableFrom(principal.getClass()))
                        throw new ClassCastException(principal + " is not assignable to " + parameter.getParameterType());
    
                    return principal;
                }
    
                <T extends Annotation> T findMethodAnnotation(Class<T> annotationClass, MethodParameter parameter) {
                    T annotation = parameter.getParameterAnnotation(annotationClass);
                    if(annotation != null) {
                        return annotation;
                    }
                    Annotation[] annotationsToSearch = parameter.getParameterAnnotations();
                    for(Annotation toSearch : annotationsToSearch) {
                        annotation = AnnotationUtils.findAnnotation(toSearch.annotationType(), annotationClass);
                        if(annotation != null) {
                            return annotation;
                        }
                    }
                    return null;
                }
            });
        }
    }
    

    And then use it in a controller

    @RequestMapping("/userget")
    public User message(@CurrentUser User user){
        return user;
    }
    

    Note that User does not require to extends UserDetails anyamore. Hope this (will) help(s)

    0 讨论(0)
  • 2021-01-13 03:47

    I haven't tried it, but this should work for you:

    @Configuration
    public class MvcConfig extends WebMvcConfigurerAdapter {
    
        @Bean
        public AuthenticationPrincipalArgumentResolver authenticationPrincipalArgumentResolver(){
            return new AuthenticationPrincipalArgumentResolver();
        }
    
        @Override
        public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
            argumentResolvers.add(authenticationPrincipalArgumentResolver());
        }
    }
    
    0 讨论(0)
  • 2021-01-13 03:55

    As mentioned in one of the comments on this post, the Account class you're returning needs to be assignable from authentication.getPrincipal() which means your Account class most likely needs to implement the UserDetails interface (as a minimum) since the org.springframework.security.core.userdetails.User class does. Look at Java's API doc for UserDetails or this page for a feel.

    If you change the type after @AuthenticationPrincipal to User, the null problem will probably go away.

    Depending on how you setup your database, having the Account class implement UserDetails might introduce too much logic into Account since it's supposed to be a model.

    Since you're using @EnableWebSecurity, you don't need to configure a custom HandlerMethodArgumentResolver as some posts suggest.

    My personal suggestion

    Note I'm using Spring Boot

    Since using jdbcAuthentication requires you to setup your database in a pre-defined way (and most likely the schema you had in mind is nothing like the one they created), your best bet is to create a custom schema and configure your own user authentication service (it's not hard). Personally, I configured a custom user authentication service in the configureGlobal... method. One liner.

    auth.userDetailsService(userService).passwordEncoder...
    

    where my custom userService implements the UserDetailsService interface which only provides one method for you to implement public UserDetails loadUserByUsername(String username) Spring Security will call that method to authenticate users. Within that loadUserByUsername method, I retrieved my user info from the database, created a new org.springframework.security.core.userdetails.User object, stuffed username, password, and permissions into the User object and returned it. I have the following at the end of my method.

    return new User(user.getUsername(), user.getPassword(), permissions);
    

    Because I returned User which implements UserDetails interface, @AuthenticationPrincipal User user will work for me.

    DO NOTE that permissions variable needs to be a class which implements the GrantedAuthority interface, or a collection of that type. That interface also has only one method for you to implement public String getAuthority(), which basically can return any string you like (presumably the names of permission / roles in your database).

    I'll assume you know how Spring Security deals with authorization. It's saddening that Spring Security uses solely Role-based authorization (if I'm wrong I'd be happy to be corrected), instead of group + permission for a more granular control. You can, however, work around that by creating a groups table and a rights table in your database and having a corresponding classes implement GrantedAuthority interface. You'll effectively be separating "roles" into those two categories.

    If you are really keen on using your own class via @AuthenticationPrincipal, or if you want more than username and password I would probably create a wrapper class around your Account class (to take logic out of Account) and have the wrapper class implement UserDetails I've done this successfully.

    Lastly, I would highly recommend adding a service layer on top of your repository layer, and get your controllers to talk to the service layer. This setup adds a layer of security (since hackers will have to hack your service layer as well before reaching your repository layer) and also takes the logic out the repository layer since it should only be used for CRUD, nothing more.

    0 讨论(0)
提交回复
热议问题