繁体   English   中英

Spring Security AuthenticationManager 设置一个 PasswordEncoder

[英]Spring Security AuthenticationManager set a PasswordEncoder

我正在使用带有 Java 14 的 SpringBoot 2.3.1.RELEASE。

我的 Spring 安全工作正常,即它可以接收用户名和密码并返回 jwt 令牌。 各种 api 调用已针对令牌成功验证。

这是我的 WebSecurityConfigurerAdapter

安全配置.java

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    @Qualifier("datasource1")
    private DataSource dataSource;

    @Autowired
    private JwtRequestFilter jwtRequestFilter;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.jdbcAuthentication().dataSource(dataSource)
        .usersByUsernameQuery("SELECT username, password, (NOT disabled) as enabled FROM members "+
                "WHERE username = ?")
        .authoritiesByUsernameQuery("SELECT m.username, t.name as authority FROM members m " +
                "JOIN administrator a ON a.member_id = m.member_id " +
                "JOIN admin_type t ON t.admin_type_id = a.admin_type_id "+
                "WHERE m.username = ?");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // set up the jwt auth
        http.cors().disable();
        http.csrf().disable().authorizeRequests().antMatchers("/authenticate").permitAll()//.anyRequest().authenticated()
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                .and().exceptionHandling()
                .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); // don't manage sessions, using jwt
        http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);

        // define the role mappings
        http.authorizeRequests()
                .antMatchers("/admin").hasAuthority("approver admin")
                .antMatchers("/approvals").hasAuthority("approver admin")
                //.antMatchers("/rest/*").hasAuthority("approver admin")
                .antMatchers("/hello").permitAll();

        // INSERT INTO admin_type (admin_type_id, name, description) VALUES ((SELECT MAX(admin_type_id) +1 FROM admin_type), 'approver admin', 'Able to alter approval requests');
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new NexctPasswordEncoder();
    }
}

我也有一个 RESTful 资源。 这会收到一个包含用户名和原始密码的AuthenticationRequest

批准Resource.java

@RequestMapping(value = "/authenticate", method = RequestMethod.POST)
public ResponseEntity<?> createAuthenticationToken(@RequestBody AuthenticationRequest authenticationRequest) throws Exception {
    try {
        authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword())
        );
    } catch (BadCredentialsException e) {
        logger.info("Incorrect username or password for "+authenticationRequest.getUsername());
        throw new Exception("Incorrect username or password", e);
    }
    final UserDetails userDetails = userDetailsService.loadUserByUsername(authenticationRequest.getUsername());
    final String jwt = jwtTokenUtil.generateToken(userDetails);
    final String username = jwtTokenUtil.extractUserName(jwt);
    logger.info("User just logged in: "+username);
    return ResponseEntity.ok(new AuthenticationResponse(jwt));
}

NexctPasswordEncoder.java

public class NexctPasswordEncoder implements PasswordEncoder {

    Logger logger = LoggerFactory.getLogger(NexctPasswordEncoder.class);

    @Override
    public String encode(CharSequence rawPassword) {
        return encodeString(rawPassword.toString());
    }

    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        String encoded = encodeString(rawPassword.toString());
        boolean match = encoded.equals(encodedPassword);
        return match;
    }

    private String encodeString(String s) {
        String encryptedPassword = null;
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("SHA-384");
            byte[] pt = s.getBytes();
            byte[] out = messageDigest.digest(pt);
            encryptedPassword = HexConvert.ByteToHexString(out);
        } catch (NoSuchAlgorithmException e) {
            logger.error("Error trying to encode password");
        }
        return encryptedPassword;
    }
}

当收到原始(未加密)密码时,这一切都与NexctPasswordEncoder完美配合。 NexctPasswordEncoder对密码进行加密,以便将其与数据库中的加密密码进行比较。

问题

当我收到加密密码而不是原始密码时,我还需要处理这种情况。

解决方案

我需要让上面的内容与两个不同的PasswordEncoder一起工作。

理想情况下,在收到带有用户名和密码的请求的ApprovalsResource中,调用以下内容:

authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword())

我想设置相关的PasswordEncoder (一个加密密码,一个不加密)。

问题

如何根据请求中的参数交换PasswordEncoder

签出 DelegatingPasswordEncoder: https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/crypto/password/DelegatingPasswordEncoderC6A235FDC3A24F027F2B63B134D6Z.

它允许根据密码值中的前缀确定要使用的其他 PasswordEncoder。

其实解决方法很简单。

您不能使用 DelegatePasswordEncoder。 因为密码需要用前缀存储。 当它们使用前缀存储时,spring 安全性将使用该前缀的密码编码器。

您可以执行以下操作之一:

  • 增强您的 PasswordEncoder 以进行纯匹配和编码匹配。
  • 为 NexctPasswordEncoder 创建一个包装器,它将进行完全匹配,然后将其作为密码编码器注入。 boolean match = encoded.equals(encodedPassword) || rawPassword.equals(encodedPassword);
public class NexctPasswordEncoder implements PasswordEncoder {

    Logger logger = LoggerFactory.getLogger(NexctPasswordEncoder.class);

    @Override
    public String encode(CharSequence rawPassword) {
        return encodeString(rawPassword.toString());
    }

    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        String encoded = encodeString(rawPassword.toString());
        boolean match = encoded.equals(encodedPassword) || rawPassword.equals(encodedPassword);
        return match;
    }

    private String encodeString(String s) {
        String encryptedPassword = null;
        try {
            MessageDigest messageDigest = MessageDigest.getInstance("SHA-384");
            byte[] pt = s.getBytes();
            byte[] out = messageDigest.digest(pt);
            encryptedPassword = HexConvert.ByteToHexString(out);
        } catch (NoSuchAlgorithmException e) {
            logger.error("Error trying to encode password");
        }
        return encryptedPassword;
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM