最近在搭建新的后台技术架构,其中旧系统使用 Spring security 4.2.1, 准备迁移到 Spring security 5.X,遇到的坑记录一下,以下将 Spring security 简称为 SS
Q1:包依赖问题(SS5 + oauth2)
Server 端 & client 端
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
Q2: 旧系统采用SS的Md5PasswordEncoder 类将明文密码进行加密(MD5编码格式),并通过自定义了salt加强密码安全性,而SS5 进行密码比对时已经取消 salt 的参数
原因:由于SS5为提高安全性,salt已由ss内部实现自动为密码生成salt,在生成出来的密码中已经包含salt内容,所以不再开发人员传入salt参数。
解决方案:查看了SS4.X版本发现其提供的salt实际上是往需加密的内容最后追加字符串 {salt} 实现,所以实现上建议写一个类进行处理
import org.springframework.security.crypto.password.MessageDigestPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
/**
* Created by chris on 2018/10/23
*/
public class CsisOldPasswordEncoder implements PasswordEncoder {
protected PasswordEncoder passwordEncoder = new MessageDigestPasswordEncoder("MD5");
private String salt ;
public CsisOldPasswordEncoder(String salt) {
this.salt = "{"+salt+"}";
}
@Override
public String encode(CharSequence rawPassword) {
return passwordEncoder.encode(rawPassword+salt);
}
@Override
public boolean matches(CharSequence rawPassword, String encodePassword) {
return passwordEncoder.matches( rawPassword+salt, encodePassword);
}
}
使用方法(单元测试):
@Test
public void test() {
// 将 salt 替换成你的实际 salt 内容
CsisOldPasswordEncoder encoder = new CsisOldPasswordEncoder("your-salt");
String encodePassword = encoder.encode("123456");
System.out.println("encodePassword="+encodePassword);
// 原系统的数据库的密码保存的内容是这样的
encodePassword = "0eba1b218483146b45b6e93c67300a31";
boolean match = encoder.matches("123456", encodePassword);
Assert.isTrue(match, "encoder not match!!");
}
Q3: 微服务之间 Authorization 传递(使用用户的SSO Header传递)
如微服务之间使用FeignClient进行接口调用(如微服务A调用微服务B),此时需要在微服务A中进行oauth2 的header 转发,否则微服务A默认不会将客户端的HEADER传递到下一个微服务,实现方法如下:
- 定义一个类实现转发header功能
public class AuthorizationForwardInterceptor implements RequestInterceptor {
@Override
public void apply(RequestTemplate template) {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
template.header(HttpHeaders.AUTHORIZATION, request.getHeader(HttpHeaders.AUTHORIZATION));
}
}
- 在APP配置类中展开对象
@Bean
RequestInterceptor oauth2FeignRequestInterceptor() {
return new AuthorizationForwardInterceptor();
}
Q4: 自定义 OAuth2 请求HEADER传递
@Bean
RequestInterceptor oauth2FeignRequestInterceptor() {
return new OAuth2FeignRequestInterceptor(new DefaultOAuth2ClientContext(), resource());
}
private OAuth2ProtectedResourceDetails resource() {
ResourceOwnerPasswordResourceDetails resourceDetails = new ResourceOwnerPasswordResourceDetails();
// resourceDetails.setUsername("username");
// resourceDetails.setPassword("password");
resourceDetails.setAccessTokenUri("http://localhost:8800/auth/oauth/token");
resourceDetails.setClientId("client");
resourceDetails.setClientSecret("secret");
resourceDetails.setGrantType("password");
resourceDetails.setScope(Arrays.asList("app"));
return resourceDetails;
}