对用户的密码进行加密以及只允许有权限的管理员进行删除用户
1. 用户密码加密
使用 springsecurity 中的 BCryptPasswordEncoder 方法对密码进行加密 (encode) 与密码匹配 (matches).
方法采用
SHA-256 + 随机盐 + 密钥
对密码进行加密.SHA 系列是 Hash 算法, 不是加密算法. (加密算法意味着可以解密, 采用 Hash 意味着其过程不可逆)
1. bCryptPasswordEncoder.encode() 对密码加密
2. bCryptPasswordEncoder.matches() 密码进行匹配
1.1 导包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
1.2 设置配置文件 SecurityConfig
加密过程所有路径都可以通过
package com.springCloud.user.config.security;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
/**
* EnableWebSecurity: Introducing Security Configuration Permissions.
*/
@EnableWebSecurity
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
/**
* Define authorization rules.
*
* @param http http
* @throws Exception Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
//super.configure(http);
/* Setting Path Access Permissions. */
/*
* authorizeRequests: 全注解配置实现的开端, 表示开始需要权限.
* 需要的权限分为两部分: 第一部分是拦截的路径, 第二部分该路径需要的访问权限.
* mvcMatchers: 表示拦截什么路径.
* permitAll: 任何权限都可以访问.
* anyRequest: 任何请求.
* authenticated: 认证后才可以访问.
* .and().csrf().disable(); 固定写法, 使 csrf 拦截失败.
* */
http.authorizeRequests()
.mvcMatchers("/**")
.permitAll()
.anyRequest()
.authenticated()
.and().csrf().disable();
}
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}
1.3 对 user 进行加密
private IdWorker idWorker;
private BCryptPasswordEncoder bCryptPasswordEncoder;
/**
* 获取容器对象
*/
@Autowired
private UserServiceImpl(IdWorker idWorker,
BCryptPasswordEncoder bCryptPasswordEncoder) {
this.idWorker = idWorker;
this.bCryptPasswordEncoder = bCryptPasswordEncoder;
}
/**
* 添加用户并保存
*
* @param user 用户
*/
@Override
public void save(User user) {
// 随机生成 id 号 (雪花算法)
user.setId(String.valueOf(idWorker.nextId()));
user.setLoginPassword(bCryptPasswordEncoder.encode(user.getLoginPassword()));
user.setPayPassword(bCryptPasswordEncoder.encode(user.getPayPassword()));
userDao.save(user);
}
1.4 对登录匹配密码
/**
* 手机号登录
*
* @param user 用户
* @return User
*/
@Override
public User login_phone(User user) {
User byPhone = userDao.findByPhone(user.getPhone());
if (byPhone != null && bCryptPasswordEncoder.matches(user.getLoginPassword(), byPhone.getLoginPassword())) {
return byPhone;
}
return null;
}
2. 有权限的管理员可以删除用户
JSON Web Token (
JWT
) 是目前最流行的跨域身份验证解决方案.
JWT
跨域身份验证.- Token 的方式做登录身份校验去取 Redis 中的缓存的用户信息.
- 服务器不保存任何会话数据, 使服务器处于
无状态
.- 使用 Base64 URL 算法将所设 JSON 对象转换为字符串保存.
- JWT 不仅可用于认证, 还可用于信息交换.
- JWT 的三个部分: JWT头, 有效载荷, 签名.
2.1 导包
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
2.2 common 模块中创建 util 类 JWT
package com.springCloud.common.util;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Date;
@ConfigurationProperties(prefix = "jwt.config")
@Component
@Data
public class JWT {
private String key;
private long expiration;
/**
* 创建 JWT
* @param id 用户的 id 号
* @param subject 用户名
* @param role 用户的角色
* @return JWT 编码.
*/
public String createJWT(String id, String subject, String role) {
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
JwtBuilder jwtBuilder = Jwts.builder()
.setId(id)
.setSubject(subject)
.setIssuedAt(now)
.signWith(SignatureAlgorithm.HS256, key)
.claim("role", role);
if (expiration > 0) {
jwtBuilder.setExpiration(new Date(nowMillis + expiration));
}
return jwtBuilder.compact();
}
/**
* 解析 JWT
* @param jwtString JWT 编码字符串
* @return String
*/
public Claims parseJWT(String jwtString) {
return Jwts.parser()
.setSigningKey(key)
.parseClaimsJws(jwtString)
.getBody();
}
}
2.3 user 模块中创建 JWT 的配置类
package com.springCloud.user.config.jwt;
import com.springCloud.common.util.JWT;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class JwtConfig {
@Bean
public JWT jwt() {
return new JWT();
}
}
2.4 user 模块中创建拦截器
将拦截器放在配置中
package com.springCloud.user.config.interceptor;
import com.springCloud.user.interceptor.JwtInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
private JwtInterceptor jwtInterceptor;
@Autowired
public InterceptorConfig(JwtInterceptor jwtInterceptor) {
this.jwtInterceptor = jwtInterceptor;
}
/**
* Registered Interceptor
*
* @param registry registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 注册拦截器要声明拦截器对象和拦截器请求.
registry.addInterceptor(jwtInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/**/login**/**");
}
}
拦截器
package com.springCloud.user.interceptor;
import com.springCloud.common.util.JWT;
import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@Component
public class JwtInterceptor implements HandlerInterceptor {
private JWT jwt;
@Autowired
private JwtInterceptor(JWT jwt) {
this.jwt = jwt;
}
/**
* 如论如何都需要放行, 而拦截需要在操作中去判断.
* login 中 拦截器只需要将请求头中包括 token 的令牌进行解析.
*
* @param request 请求
* @param response 响应
* @param handler 处理
* @return boolean
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
// 得到请求头中的 Authorization.
String header = request.getHeader("Authorization");
// 判断是否进行拦截
if (header != null && !"".equals(header) && header.startsWith("Bearer ")) {
// 得到 token
String token = header.substring(7);
// 进行对 token 解析.
try {
Claims claims = jwt.parseJWT(token);
// 得到登录的角色
String role = (String) claims.get("role");
if ("admin".equals(role)) {
request.setAttribute("token_admin", token);
}
if ("user".equals(role)) {
request.setAttribute("token_user", token);
}
} catch (Exception e) {
throw new RuntimeException("令牌不正确");
}
}
return true;
}
}
2.5 user 模块中的 controller
private UserService userService;
private RedisTemplate<String, String> redisTemplate;
private HttpServletRequest request;
private JWT jwt;
/**
* 获取容器对象
*
* @param userService 识别 userService 容器对象
*/
@Autowired
private UserController(UserService userService,
RedisTemplate<String, String> redisTemplate,
HttpServletRequest request,
JWT jwt) {
this.userService = userService;
this.redisTemplate = redisTemplate;
this.request = request;
this.jwt = jwt;
}
/**
* 登录用户时需要前后端通话的操作 JWT
* @param user 用户
* @return Result
*/
private Result login_jwt(User user) {
String token = this.jwt.createJWT(user.getId(), user.getName(), "user");
Map<String, Object> map = new HashMap<>();
map.put("role", "user");
map.put("token", token);
return new Result(StatusCode.OK, true, "用户登录成功", map);
}
/**
* 用户名登录
*
* @param user 用户
* @return Result
*/
@PostMapping("/login_name")
public Result login_name(@RequestBody User user) {
User login = userService.login_name(user);
if (login == null) {
return new Result(StatusCode.LOGIN_ERROR, false, "用户登录失败");
}
// 登录成功了, 需要进行前后端通话的操作. JWT
return login_jwt(user);
}
/**
* 手机号登录
*
* @param user 用户
* @return Result
*/
@PostMapping("/login_phone")
public Result login_phone(@RequestBody User user) {
User login = userService.login_phone(user);
if (login == null) {
return new Result(StatusCode.LOGIN_ERROR, false, "用户登录失败");
}
// 登录成功了, 需要进行前后端通话的操作. JWT
return login_jwt(user);
}
同理: admin 也一样的.