Spring security 4.x 往 5.x 升级的坑

2018-10-24  本文已影响0人  狼團

最近在搭建新的后台技术架构,其中旧系统使用 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传递到下一个微服务,实现方法如下:

  1. 定义一个类实现转发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));
    }
}
  1. 在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;
    }

上一篇下一篇

猜你喜欢

热点阅读