一 参考文章
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 注册后的数据库
五 核心代码
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用户登录,调试关键步骤如下: