spring – 使用EnableWebSecurity时,AuthenticationPrincipal为空

前端之家收集整理的这篇文章主要介绍了spring – 使用EnableWebSecurity时,AuthenticationPrincipal为空前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

截至Spring Security doc: 34.1 @EnableWebMvcSecurity州,@ EnableWebMvcSecurity被@EnableWebSecurity取代.

但是当我尝试通过@AuthenticationPrincipal在控制器中获取UserDetails时,我得到了一个空对象:用户名为“”.我也尝试了@EnableWebMvcSecurity,但不幸的是UserDetails是null.

但我可以通过传统方式获取UserDetails,如下所示:

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

我的问题是,当我使用@EnableWebSecurity时,获取自定义UserDetails(帐户)的正确方法是什么?

以下是相关的源代码

控制器:

@RequestMapping(method = RequestMethod.POST)
@Secured("ROLE_USER")
public String postRoom(@Valid @modelattribute Room room,BindingResult result,Model model,@AuthenticationPrincipal Account principal) {
    if (result.hasErrors()) {
        return "room_form";
    }

    Account account = accountRepository.findByUsername(principal.getUsername());
    room.setAccountId(account.getId());
    room.setLastModified(new Date());
    roomRepository.save(room);
    return "room_list";
}

安全配置:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private DataSource dataSource;

    @Autowired
    private SecurityProperties security;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().permitAll()
            .and().formLogin().loginPage("/login").failureUrl("/login?error").permitAll()
            .and().logout().permitAll()
            .and().rememberMe()
            .and().csrf().disable();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
            auth.jdbcAuthentication().dataSource(this.dataSource).passwordEncoder(new BCryptPasswordEncoder(8));
    }
}

而Account.java:

@Entity
@Table(name = "users")
public class Account implements Serializable {
    @Id
    @GeneratedValue
    private Long id;

    private String username;
    private String password;
    private boolean enabled;

    @Lob
    private byte[] avatar;

// getter / setter ...
}
最佳答案
正如在此post的一条评论中所提到的,您返回的Account类需要从authentication.getPrincipal()中分配,这意味着您的Account类很可能需要自组织以来实现UserDetails接口(至少). springframework.security.core.userdetails.User类可以.查看用于UserDetails的Java API文档或此page的感觉.

如果在@AuthenticationPrincipal之后将类型更改为User,则null问题可能会消失.

根据您设置数据库的方式,让Account类实现UserDetails可能会在Account中引入太多逻辑,因为它应该是一个模型.

由于您使用的是@EnableWebSecurity,因此您无需像某些帖子所建议的那样配置自定义HandlerMethodArgumentResolver.

我个人的建议

注意我正在使用Spring Boot

由于使用jdbcAuthentication要求您以预定义的方式设置数据库(并且很可能您所考虑的模式与他们创建的模式完全不同),最好的办法是创建自定义模式并配置自己的用户身份验证服务(这并不难).就个人而言,我在configureGlobal …方法中配置了自定义用户身份验证服务.一个班轮.

auth.userDetailsService(userService).passwordEncoder...

我的自定义userService实现UserDetailsS​​ervice接口,它只提供一种方法来实现公共UserDetails loadUserByUsername(String username)Spring Security将调用方法来验证用户.在该loadUserByUsername方法中,我从数据库中检索了我的用户信息,在User对象中创建了一个新的org.springframework.security.core.userdetails.User对象,填充用户名,密码和权限并将其返回.
我的方法结束时有以下内容.

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

因为我返回了实现UserDetails接口的User,所以@AuthenticationPrincipal User用户将为我工作.

请注意,权限变量必须是实现GrantedAuthority接口的类,或者是该类型的集合.该接口也只有一种方法可以实现公共String getAuthority(),它基本上可以返回您喜欢的任何字符串(可能是数据库中的权限/角色的名称).

我假设你知道Spring Security如何处理授权.令人遗憾的是,Spring Security仅使用基于角色的授权(如果我错了,我会很乐意予以纠正),而不是组许可来进行更精细的控制.但是,您可以通过在数据库中创建组表和权限表并使用相应的类实现GrantedAuthority接口来解决此问题.您将有效地将“角色”分为这两类.

如果您真的热衷于通过@AuthenticationPrincipal使用自己的类,或者您想要的不仅仅是用户名和密码
我可能会围绕你的Account类创建一个包装类(从Account中取出逻辑)并让包装类实现UserDetails我已经成功完成了这个.

最后,我强烈建议在存储库层的顶部添加服务层,并让控制器与服务层进行通信.这个设置增加了一层安全性(因为黑客在到达你的存储库层之前也必须破解你的服务层)并且还将逻辑从存储库层中取出,因为它只应该用于CRUD,仅此而已.

猜你在找的Spring相关文章