本文个人博客地址:https://www.leafage.top/posts/detail/21697I2R

最近几天在改造项目,需要将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 org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.core.userdetails.ReactiveUserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.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 加密方式*/@Beanprotected PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}/*** 用户数据加载** @return JdbcReactiveUserDetailsService 接口*/@Beanpublic ReactiveUserDetailsService userDetailsService() {// 自定义的ReactiveUserDetails 实现return new JdbcReactiveUserDetailsService(hypervisorApi);}/*** 安全配置*/@BeanSecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {http.formLogin(f -> f.authenticationSuccessHandler(authenticationSuccessHandler()).authenticationFailureHandler(authenticationFailureHandler())).logout(l -> l.logoutSuccessHandler(new HttpStatusReturningServerLogoutSuccessHandler())).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 cn.hutool.crypto.SecureUtil;
import com.ichinae.imis.gateway.utils.SaltUtil;
import org.springframework.security.crypto.codec.Utf8;
import org.springframework.security.crypto.password.PasswordEncoder;
import java.security.MessageDigest;/*** 自定义加密解密*/
public class MD5PasswordEncoder implements PasswordEncoder {@Overridepublic String encode(CharSequence charSequence) {String salt = SaltUtil.generateSalt();return SecureUtil.md5(SecureUtil.md5(charSequence.toString()) + salt);}@Overridepublic boolean matches(CharSequence charSequence, String encodedPassword) {byte[] expectedBytes = bytesUtf8(charSequence.toString());byte[] actualBytes = bytesUtf8(charSequence.toString());return MessageDigest.isEqual(expectedBytes, actualBytes);}private static byte[] bytesUtf8(String s) {// need to check if Utf8.encode() runs in constant time (probably not).// This may leak length of string.return (s != null) ? Utf8.encode(s) : null;}}

第二个问题的解决办法,找了很多资料,也没有找到,后来查看security的源码发现,可以在UserDetailsService接口的findByUsername()方法中,在返回UserDetails实现的时候,使用默认实现User的UserBuilder内部类来解决这个问题,因为UserBuilder类中有一个属性,passwordEncoder属性,它是Fucntion<String, String>类型的,默认实现是 password -> password,即对密码不做任何处理,先看下它的源码:

再看下解决问题之前的findByUsername()方法:

@Service
public class UserDetailsServiceImpl implements ReactiveUserDetailsService {@Resourceprivate RemoteService remoteService;@Overridepublic Mono<UserDetails> findByUsername(String username) {return remoteService.getUser(username).map(userBO -> User.builder().username(username).password(userBO.getPassword()).authorities(grantedAuthorities(userBO.getAuthorities())).build());}private Set<GrantedAuthority> grantedAuthorities(Set<String> authorities) {return authorities.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toSet());}}

那找到了问题的解决方法,就来改代码了,如下所示:

新增一个代码处理方法

private Function<String, String> passwordEncoder(String salt) {return rawPassword -> SecureUtil.md5(rawPassword + salt);
}

然后添加builder链

@Service
public class UserDetailsServiceImpl implements ReactiveUserDetailsService {@Resourceprivate RemoteService remoteService;@Overridepublic Mono<UserDetails> findByUsername(String username) {return remoteService.getUser(username).map(userBO -> User.builder().passwordEncoder(passwordEncoder(userBO.getSalt())) //在这里设置动态的盐.username(username).password(userBO.getPassword()).authorities(grantedAuthorities(userBO.getAuthorities())).build());}private Set<GrantedAuthority> grantedAuthorities(Set<String> authorities) {return authorities.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toSet());}private Function<String, String> passwordEncoder(String salt) {return rawPassword -> SecureUtil.md5(rawPassword + salt);}
}

然后跑一下代码,请求登录接口,就登陆成功了。

Spring Security 密码验证动态加盐的验证处理相关推荐

  1. MD5加密以及验证加密-加盐

    加密与解密算法: /// <summary> /// 签名字符串 32位 /// </summary> /// <param name="input" ...

  2. 学成在线-第16天-讲义- Spring Security Oauth2 JWT RSA加解密

    学成在线-第16天-讲义- Spring Security Oauth2 JWT 1 用户认证需求分析 1.1 用户认证与授权 ​ 截至目前,项目已经完成了在线学习功能,用户通过在线学习页面点播视频进 ...

  3. Spring Security 4 整合Hibernate 实现持久化登录验证(带源码)

    上一篇文章:Spring Security 4 整合Hibernate Bcrypt密码加密(带源码) 原文地址:http://websystique.com/spring-security/spri ...

  4. java取消 验证_使用Spring Security Java配置时禁用基本身份验证

    我正在尝试使用Spring Security Java配置保护Web应用程序的安全. 这是配置的样子: @Configuration @EnableWebMvcSecurity public clas ...

  5. 密码认证-SHA加盐密码

    密码认证,首先是密码加密算法,然后密码咋存储 ,如图所示 密码加密分为不可加密和可逆加密算法.安全存储策略,有明文保存和加盐保存,明文保持容易导致密码丢失,一般采用加盐密码保持.注册和认证流程如下: ...

  6. SpringBoot Security 自定义登录验证逻辑+密码加盐

    密码加盐思路 JAVA 加盐加密方法_Teln_小凯的博客-CSDN博客 盐加密方法 @ApiOperation(value = "002-加密")@PreAuthorize(&q ...

  7. Spring Security 详解与实操第一节 认证体系与密码安全

    开篇词 Spring Security,为你的应用安全与职业之路保驾护航 你好,我是鉴湘,拉勾教育专栏<Spring Cloud 原理与实战><Spring Boot 实战开发> ...

  8. 国产加密实际运用:使用SM3加盐存储密码,并且使用SM2进行登录认证

    目录 1.简要 2.开发环境及工具 3.后台密码加密部分 3.1加密代码 3.2 SM3加密类(Sm3crypto) 3.3国密SM3工具类(Sm3Utils) 3.4国密相关依赖包 4.登录认证部分 ...

  9. Spring Security 之密码存储

    相关文章: OAuth2的定义和运行流程 Spring Security OAuth实现Gitee快捷登录 Spring Security OAuth实现GitHub快捷登录 Spring Secur ...

最新文章

  1. Linux导入ora-39070,impdp 导入数据报ORA-39002,ORA-39070,ORA-39002, ORA-39087
  2. 用easyx画电子钟_Canvas入门-利用Canvas绘制好玩的电子时钟
  3. 下一个视觉「杀手级应用」来了,依图行人重识别ReID性能创纪录
  4. [Struts]Cannot find bean in any scope之一解
  5. Intellij IDEA 快捷键整理
  6. 源码安装postgresql
  7. java build.gradle 详解_java – Gradle构建失败[无法解析配置的所有依赖项’:compile’.]...
  8. boost::hana::count_if用法的测试程序
  9. Java面试题 Java中sleep和wait的区别?
  10. RabbitMQ指南之四:路由(Routing)和直连交换机(Direct Exchange)
  11. 五年级计算机下册工作计划,五年级下学期工作计划
  12. 七、基于比较的排序算法总结
  13. 开两个服务内存溢出_详解JVM内存区域
  14. ps中怎么导出tif_PS导入文件和导出文件教程-tif文件
  15. pgpool-II 手册
  16. 【推荐五款ssh连接工具】
  17. Java整合科大讯飞SDK实现语音转文字
  18. 首页-文章列表 (三) -列表文章布局-Cell 单元格组件-一张图和三张图 van-ellipsis 内容超过一行会省略 w33-宽度33%剩余1%做了图片之间间隙.md
  19. C++扑克牌类的设计
  20. 新房装修流程详细步骤有哪些? 新房装修流程注意事项有哪些?

热门文章

  1. 如何确保有效的软件质量管理流程
  2. 3a企业信用等级证书有什么用?
  3. 解读支持向量机SVM
  4. html select 样式t调整_css修改html select下拉框样式(含右边箭头)
  5. 入门数据结构,看这一篇就够了!
  6. PHPRunner 10.7.0 PHP代码生成器
  7. Power Assist Wear
  8. Python:天干地支
  9. 中国征信行业市场发展模式及未来趋势展望报告2022-2028年
  10. 8750H带的动MATLAB2019吗,比7820HK略弱 8代酷睿i7-8750H处理器对比7代跑分测试