Spring Security的密码加密实战

一 参考文章

http://www.spring4all.com/article/421

二 代码位置

https://github.com/cakin24/spring-security-demos/tree/master/01%20-%20%E5%AF%86%E7%A0%81%E5%8A%A0%E5%AF%86%EF%BC%88%E6%95%B0%E6%8D%AE%E5%BA%93%EF%BC%89

三 数据库

1 新建数据库security01

2 新建数据表

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(255) DEFAULT NULL COMMENT '账号',
  `password` varchar(255) DEFAULT NULL COMMENT '密码',
  `nickname` varchar(255) DEFAULT '' COMMENT '昵称',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

四 测试

1 启动程序

2 浏览器输入  http://localhost:8080/register

3 注册

用户名 密码 昵称
admin admin admin
user user user
cakin cakin cakin

 

 

 

 

 

4 注册后的数据库

Spring Security的密码加密实战_第1张图片

五 核心代码

package com.spring4all.service.impl;


import com.spring4all.entity.UserDO;
import com.spring4all.repository.UserRepository;
import com.spring4all.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Primary;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.crypto.password.Pbkdf2PasswordEncoder;
import org.springframework.security.crypto.password.StandardPasswordEncoder;
import org.springframework.security.crypto.scrypt.SCryptPasswordEncoder;
import org.springframework.stereotype.Service;


import java.util.HashMap;
import java.util.Map;
import java.util.Random;


@Service
@Primary
@Slf4j
public class BaseUserService implements UserService {


    private final static Map ENCODER_TYPE = new HashMap<>();


    private final static Map ENCODER_MAP = new HashMap<>();


    private final static String PASSWORD_FORMAT = "{%s}%s";


    private final UserRepository userRepository;


    public BaseUserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }


    static {
        ENCODER_TYPE.put(0, "noop");   // 不加密
        // 下面是4种常用的加密算法
        ENCODER_TYPE.put(1, "bcrypt");
        ENCODER_TYPE.put(2, "pbkdf2");
        ENCODER_TYPE.put(3, "scrypt");
        ENCODER_TYPE.put(4, "sha256");
        ENCODER_MAP.put("noop", NoOpPasswordEncoder.getInstance());
        ENCODER_MAP.put("bcrypt", new BCryptPasswordEncoder());
        ENCODER_MAP.put("pbkdf2", new Pbkdf2PasswordEncoder());
        ENCODER_MAP.put("scrypt", new SCryptPasswordEncoder());
        ENCODER_MAP.put("sha256", new StandardPasswordEncoder());
    }


    @Override
    public void insert(UserDO userDO) {
        String username = userDO.getUsername();
        if (exist(username)) {
            throw new RuntimeException("用户名已存在!");
        }
        // 随机使用加密方式
        Random random = new Random();
        int x = random.nextInt(5);
        String encoderType = ENCODER_TYPE.get(x);
        PasswordEncoder passwordEncoder = ENCODER_MAP.get(encoderType);
        userDO.setPassword(String.format(PASSWORD_FORMAT, encoderType, passwordEncoder.encode(userDO.getPassword())));
        userRepository.save(userDO);
    }


    @Override
    public UserDO getByUsername(String username) {
        return userRepository.findByUsername(username);
    }


    /**
     * 判断用户是否存在
     */
    private boolean exist(String username) {
        UserDO userDO = userRepository.findByUsername(username);
        return (userDO != null);
    }
}

这里用到了随机加密方式

六 源码解读

/org/springframework/security/authentication/dao/DaoAuthenticationProvider.java

protected void additionalAuthenticationChecks(UserDetails userDetails,
      UsernamePasswordAuthenticationToken authentication)
      throws AuthenticationException {
   if (authentication.getCredentials() == null) {
      logger.debug("Authentication failed: no credentials provided");


      throw new BadCredentialsException(messages.getMessage(
            "AbstractUserDetailsAuthenticationProvider.badCredentials",
            "Bad credentials"));
   }


   String presentedPassword = authentication.getCredentials().toString();
   // 就是这里对密码进行加密比较的
   if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
      logger.debug("Authentication failed: password does not match stored value");


      throw new BadCredentialsException(messages.getMessage(
            "AbstractUserDetailsAuthenticationProvider.badCredentials",
            "Bad credentials"));
   }
}

七 调试

用admin用户登录,调试关键步骤如下:

Spring Security的密码加密实战_第2张图片

 

你可能感兴趣的:(Spring,Security)