[英]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 安全性将使用该前缀的密码编码器。
您可以执行以下操作之一:
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.