有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java Spring Security AuthenticationManager设置密码编码器

我使用的是SpringBoot 2.3.1。使用Java14发布

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

这是我的Web安全配置适配器

SecurityConfig。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

批准资源。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));
}

nextPasswordEncoder。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


共 (2) 个答案

  1. # 1 楼答案

    实际上,解决方案非常简单

    You can't use DelegatePasswordEncoder. Because passwords need to be stored with prefix. And when they stored with prefix, spring security will use the password encoder for that prefix.

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

    • 增强您的密码编码器,以进行普通匹配和编码匹配
    • 为NextPasswordEncoder创建一个包装器,它将进行精确匹配,然后将其作为密码编码器注入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;
        }
    }