使用 Spring 数据 REST 的密码编码
Password encoding with Spring Data REST
我应该如何使用 Spring 数据 REST 自动编码我实体提交的普通密码字段?
我正在使用 BCrypt 编码器,当客户端通过 POST、PUT 和 PATCH 发送请求时,我想自动对请求的密码字段进行编码。
@Entity
public class User {
@NotNull
private String username;
@NotNull
private String passwordHash;
...
getters/setters/etc
...
}
首先,我尝试使用@HandleBeforeCreate 和@HandleBeforeSave 事件侦听器来解决,但是它的参数中的用户已经合并,所以我无法区分用户的新密码或旧密码哈希:
@HandleBeforeSave
protected void onBeforeSave(User user) {
if (user.getPassword() != null) {
account.setPassword(passwordEncoder.encode(account.getPassword()));
}
super.onBeforeSave(account);
}
是否可以在 setter 方法上使用 @Projection 和 SpEL?
密码字段修改setter方法即可,如下图:
public void setPassword(String password) {
PasswordEncoder encoder = new BCryptPasswordEncoder();
this.password = encoder.encode(password);
}
您可以实施 Jackson JsonDeserializer:
public class BCryptPasswordDeserializer extends JsonDeserializer<String> {
public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
ObjectCodec oc = jsonParser.getCodec();
JsonNode node = oc.readTree(jsonParser);
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
String encodedPassword = encoder.encode(node.asText());
return encodedPassword;
}
}
并将其应用于您的 JPA 实体 属性:
// The value of the password will always have a length of
// 60 thanks to BCrypt
@Size(min = 60, max = 60)
@Column(name="password", nullable = false, length = 60)
@JsonDeserialize(using = BCryptPasswordDeserializer.class )
private String password;
对@robgmills 的一些增强 JsonDeserializer
解决方案:
- 中Spring5介绍
DelegatingPasswordEncoder
。它更灵活,参见spring docs。
- 不需要每次反序列化时都创建
PasswordEncoder
。
- 一个大项目可能有几个
JsonDeserializer
- 最好将它们放在内部 类.
- 通常为获取请求隐藏编码密码。我用过
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
,见
对于 Spring 引导代码如下所示:
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
public static final PasswordEncoder PASSWORD_ENCODER = PasswordEncoderFactories.createDelegatingPasswordEncoder();
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService()).passwordEncoder(PASSWORD_ENCODER);
}
....
public class JsonDeserializers {
public static class PasswordDeserializer extends JsonDeserializer<String> {
public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
ObjectCodec oc = jsonParser.getCodec();
JsonNode node = oc.readTree(jsonParser);
String rawPassword = node.asText();
return WebSecurityConfig.PASSWORD_ENCODER.encode(rawPassword);
}
}
...
@Entity
public class User ...
@Column(name = "password")
@Size(max = 256)
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
@JsonDeserialize(using = JsonDeserializers.PasswordDeserializer.class)
private String password;
...
我应该如何使用 Spring 数据 REST 自动编码我实体提交的普通密码字段?
我正在使用 BCrypt 编码器,当客户端通过 POST、PUT 和 PATCH 发送请求时,我想自动对请求的密码字段进行编码。
@Entity
public class User {
@NotNull
private String username;
@NotNull
private String passwordHash;
...
getters/setters/etc
...
}
首先,我尝试使用@HandleBeforeCreate 和@HandleBeforeSave 事件侦听器来解决,但是它的参数中的用户已经合并,所以我无法区分用户的新密码或旧密码哈希:
@HandleBeforeSave
protected void onBeforeSave(User user) {
if (user.getPassword() != null) {
account.setPassword(passwordEncoder.encode(account.getPassword()));
}
super.onBeforeSave(account);
}
是否可以在 setter 方法上使用 @Projection 和 SpEL?
密码字段修改setter方法即可,如下图:
public void setPassword(String password) {
PasswordEncoder encoder = new BCryptPasswordEncoder();
this.password = encoder.encode(password);
}
您可以实施 Jackson JsonDeserializer:
public class BCryptPasswordDeserializer extends JsonDeserializer<String> {
public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
ObjectCodec oc = jsonParser.getCodec();
JsonNode node = oc.readTree(jsonParser);
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
String encodedPassword = encoder.encode(node.asText());
return encodedPassword;
}
}
并将其应用于您的 JPA 实体 属性:
// The value of the password will always have a length of
// 60 thanks to BCrypt
@Size(min = 60, max = 60)
@Column(name="password", nullable = false, length = 60)
@JsonDeserialize(using = BCryptPasswordDeserializer.class )
private String password;
对@robgmills 的一些增强 JsonDeserializer
解决方案:
- 中Spring5介绍
DelegatingPasswordEncoder
。它更灵活,参见spring docs。 - 不需要每次反序列化时都创建
PasswordEncoder
。 - 一个大项目可能有几个
JsonDeserializer
- 最好将它们放在内部 类. - 通常为获取请求隐藏编码密码。我用过
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
,见
对于 Spring 引导代码如下所示:
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
public static final PasswordEncoder PASSWORD_ENCODER = PasswordEncoderFactories.createDelegatingPasswordEncoder();
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService()).passwordEncoder(PASSWORD_ENCODER);
}
....
public class JsonDeserializers {
public static class PasswordDeserializer extends JsonDeserializer<String> {
public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
ObjectCodec oc = jsonParser.getCodec();
JsonNode node = oc.readTree(jsonParser);
String rawPassword = node.asText();
return WebSecurityConfig.PASSWORD_ENCODER.encode(rawPassword);
}
}
...
@Entity
public class User ...
@Column(name = "password")
@Size(max = 256)
@JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
@JsonDeserialize(using = JsonDeserializers.PasswordDeserializer.class)
private String password;
...