【问题标题】:SecurityContextHolder.getContext().getAuthentication().getCredentials() returns null after authenticationSecurityContextHolder.getContext().getAuthentication().getCredentials() 认证后返回 null
【发布时间】:2019-04-28 23:49:55
【问题描述】:

我创建了一个简单的 spring web mvc 应用程序,但我遇到了一个问题。身份验证后,我尝试获取身份验证对象,但由于某种原因,它的凭据为空。

在这个项目中,我有一个自定义的 AuthenticationProvider,如下所示:

@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private UserService userService;

    @Autowired
    private RoleService roleService;

    @PostConstruct
    public void init() {
        roleService.AddStandardRolesIfNeeded();
        userService.AddUserWithAdminRoleIfNotExists("a"); 
    }


    public Authentication authenticate(Authentication authentication) throws AuthenticationException {


        Object credentials = authentication.getCredentials();
        if(!(credentials instanceof String)) {
            return null;
        }

        String username = authentication.getName();
        String password = credentials.toString(); //password isn't null here


        User user = userService.findByUsernameAndPassword(username, password);


        if(user == null) {
            throw new BadCredentialsException("Authentication failed for " + username);
        }


        List<GrantedAuthority> authorities = new ArrayList<>();
        for(Role role : user.getRoles()) {
            authorities.add(new SimpleGrantedAuthority(role.getName()));
        }

        Authentication auth = new 
                UsernamePasswordAuthenticationToken(username, password, authorities);


        return auth;
    }

    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }

}

我感兴趣的是它是在 Spring Security 中故意完成的,还是我遗漏了一些东西。

【问题讨论】:

  • 为什么要按用户名和密码搜索?您应该只按用户名搜索。
  • 然后在 authenticate() 方法中比较密码?

标签: java spring-mvc spring-security


【解决方案1】:

因为认证成功后凭证会被删除,所以必须将eraseCredentials(false)添加到AuthenticationManagerBuilder配置中。就像下面的代码:

@Autowired
private CustomAuthenticationProvider customAuthProvider;

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.authenticationProvider(customAuthProvider).eraseCredentials(false);
}

这已经晚了,但我认为这是根据问题的正确答案。

【讨论】:

  • 我已经用这个配置试了几次,它对我不起作用,getCredentials() 返回 null
  • 你能详细说明一下customAuthProvider吗?那是什么类型的物体?它应该实现/扩展什么?
  • customAuthProvider 是问题中提到的CustomAuthenticationProvider 的对象。我已经更新了答案。
  • 注意 - 这对我来说感觉很糟糕:凭据在验证后应该被删除,因此漏洞(如 Heartbleed)不能泄露所有登录用户的明文密码。最好验证一个会话令牌——但我还不知道如何正确地做到这一点;这就是我在 SO atm 上的原因。
【解决方案2】:

我通过将用户对象传递给UsernamePasswordAuthenticationToken 构造函数而不是用户名代替主体来解决这个问题。

我改变了这个:

Authentication auth = new 
                UsernamePasswordAuthenticationToken(username, password, authorities);

到这里:

Authentication auth = new 
                    UsernamePasswordAuthenticationToken(user, password, authorities);

在控制器中,我得到了用户:

User user = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();

【讨论】:

  • 您的解释表明我可以在 Principal 中传递任何恰好是 Object 数据类型的东西。赞一个。
猜你喜欢
  • 2014-04-07
  • 2013-07-08
  • 2015-08-17
  • 2019-06-29
  • 2017-04-25
  • 1970-01-01
  • 2013-06-01
  • 1970-01-01
相关资源
最近更新 更多