SpringSecurity密码验证动态加盐的验证处理⽅法
最近⼏天在改造项⽬,需要将gateway整合security在⼀起进⾏认证和鉴权,之前gateway和auth是两个服务,auth是shiro写的⼀个,⼀个filter和⼀个配置,内容很简单,⽣成token,验证token,没有其他的安全检查,然后让对项⽬进⾏重构。
先是要整合gateway和shiro,然⽽因为gateway是webflux,⽽shiro-spring是webmvc,所以没搞成功,如果有做过并成功的,请告诉我如何进⾏整合,⾮常感谢。
那整合security呢,因为spring cloud gateway基于webflux,所以⽹上很多教程是⽤不了的,webflux的配置会有⼀些变化,具体看如下代码⽰例:
import io.leafage.gateway.api.HypervisorApi;
import io.leafage.gateway.handler.ServerFailureHandler;
import io.leafage.gateway.handler.ServerSuccessHandler;
import io.leafage.gateway.service.JdbcReactiveUserDetailsService;
import t.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.fig.active.EnableWebFluxSecurity;
import org.fig.web.server.ServerHttpSecurity;
import org.userdetails.ReactiveUserDetailsService;
import org.pto.bcrypt.BCryptPasswordEncoder;
import org.pto.password.PasswordEncoder;
import org.springframework.security.web.server.SecurityWebFilterChain;
import org.springframework.security.web.server.authentication.HttpStatusServerEntryPoint;
import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler;
import org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler;
import org.springframework.security.web.server.authentication.logout.HttpStatusReturningServerLogoutSuccessHandler;
import org.springframework.security.web.server.csrf.CookieServerCsrfTokenRepository;
/**
* spring security config .
*
* @author liwenqiang 2019/7/12 17:51
*/
@EnableWebFluxSecurity
public class ServerSecurityConfiguration {
// ⽤于获取远程数据
private final HypervisorApi hypervisorApi;
public ServerSecurityConfiguration(HypervisorApi hypervisorApi) {
this.hypervisorApi = hypervisorApi;
}
/**
* 密码配置,使⽤BCryptPasswordEncoder
*
* @return BCryptPasswordEncoder 加密⽅式
*/
@Bean
protected PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* ⽤户数据加载
*
* @return JdbcReactiveUserDetailsService 接⼝
*/
@Bean
public ReactiveUserDetailsService userDetailsService() {
// ⾃定义的ReactiveUserDetails 实现
return new JdbcReactiveUserDetailsService(hypervisorApi);
}
/
**
* 安全配置
*/
@Bean
SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http.formLogin(f -> f.authenticationSuccessHandler(authenticationSuccessHandler())
.authenticationFailureHandler(authenticationFailureHandler()))
.logout(l -> l.logoutSuccessHandler(new HttpStatusReturningServerLogoutSuccessHandler()))shiro安全框架
.csrf(c -> c.csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse()))
.authorizeExchange(a -> a.pathMatchers(HttpMethod.OPTIONS).permitAll()
.anyExchange().authenticated())
.
exceptionHandling(e -> e.authenticationEntryPoint(new HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED)));
return http.build();
}
/**
* 登陆成功后执⾏的处理器
*/
private ServerAuthenticationSuccessHandler authenticationSuccessHandler() {
return new ServerSuccessHandler();
}
/**
* 登陆失败后执⾏的处理器
*/
private ServerAuthenticationFailureHandler authenticationFailureHandler() {
return new ServerFailureHandler();
}
}
上⾯的⽰例代码,是我开源项⽬中的⼀段,⼀般的配置就如上⾯写的,就可以使⽤了,但是由于我们之前的项⽬中的是shiro,然后有⼀个⾃定义的加密解密的逻辑。
⾸先说明⼀下情况,之前那⼀套加密(前端MD5,不加盐,然后数据库存储的是加盐后的数据和对应的盐(每个
账号⼀个),要登录⽐较之前对密码要获取动态的盐,然后加盐进⾏MD5,再进⾏对⽐,但是在配置的时候是没
法获取某⼀⽤户的盐值)
所以上⾯的⼀版配置是没法通过验证的,必须在验证之前,给请求的密码混合该账号对应的盐进⾏⼆次加密后在对⽐,但是这⾥就有问题了:
1. security 框架提供的⼏个加密\解密⼯具没有MD5的⽅式;
2. security 配置加密\解密⽅式的时候,⽆法填⼊动态的账号的加密盐;
对于第⼀个问题还好处理,解决⽅式是:⾃定义加密\解密⽅式,然后注⼊到配置类中,⽰例如下:
import pto.SecureUtil;
import com.ichinae.imis.gateway.utils.SaltUtil;
import org.dec.Utf8;
import org.pto.password.PasswordEncoder;
import java.security.MessageDigest;
/**
* ⾃定义加密解密
*/
public class MD5PasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence charSequence) {
String salt = ateSalt();
return SecureUtil.md5(SecureUtil.String()) + salt);
}
@Override
public boolean matches(CharSequence charSequence, String encodedPassword) {
byte[] expectedBytes = String());
byte[] actualBytes = String());
return MessageDigest.isEqual(expectedBytes, actualBytes);
}
private static byte[] bytesUtf8(String s) {
// need to check de() runs in constant time (probably not).
// This may leak length of string.
return (s != null) ? de(s) : null;
}
}
第⼆个问题的解决办法,了很多资料,也没有到,后来查看security的源码发现,可以在UserDetailsService接⼝的findByUsername()⽅法中,在返回UserDetails实现的时候,使⽤默认实现User的UserBuilder内部类来解决这个问题,因为UserBuilder类中有⼀个属性,passwordEncoder属性,它是Fucntion<String, String>类型的,默认实现是 password -> password,即对密码不做任何处理,先看下它的源码:
再看下解决问题之前的findByUsername()⽅法:
@Service
public class UserDetailsServiceImpl implements ReactiveUserDetailsService {
@Resource
private RemoteService remoteService;
@Override
public Mono<UserDetails> findByUsername(String username) {
User(username).map(userBO -> User.builder()
.username(username)
.Password())
.authorities(Authorities()))
.
build());
}
private Set<GrantedAuthority> grantedAuthorities(Set<String> authorities) {
return authorities.stream().map(SimpleGrantedAuthority::new).Set());    }
}
那到了问题的解决⽅法,就来改代码了,如下所⽰:
新增⼀个代码处理⽅法
private Function<String, String> passwordEncoder(String salt) {
return rawPassword -> SecureUtil.md5(rawPassword + salt);
}
然后添加builder链
@Service
public class UserDetailsServiceImpl implements ReactiveUserDetailsService {
@Resource
private RemoteService remoteService;
@Override
public Mono<UserDetails> findByUsername(String username) {
User(username).map(userBO -> User.builder()
.passwordEncoder(Salt())) //在这⾥设置动态的盐
.username(username)
.Password())
.authorities(Authorities()))
.
build());
}
private Set<GrantedAuthority> grantedAuthorities(Set<String> authorities) {
return authorities.stream().map(SimpleGrantedAuthority::new).Set());
}
private Function<String, String> passwordEncoder(String salt) {
return rawPassword -> SecureUtil.md5(rawPassword + salt);
}
}
然后跑⼀下代码,请求登录接⼝,就登陆成功了。
以上就是Spring Security 密码验证动态加盐的验证处理的详细内容,更多关于Spring Security密码验证的资料请关注其它相关⽂章!

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。