1.BCrypt密码加密
密码应该通过哈希算法进行加密。 有很多标准的算法比如SHA或者MD5,结合salt(盐)是一个不错的选择。 Spring Security 提供了BCryptPasswordEncoder类,实现Spring的PasswordEncoder接口使用BCrypt强 哈希方法来加密密码。
2.优点
BCrypt强哈希方法 每次加密的结果都不一样
3.springBoot中使用BCrypt密码加密登陆操作
(1)坐标导入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring‐boot‐starter‐security</artifactId>
</dependency>
(2)添加配置类
我们在添加了spring security依赖后,所有的地址都被spring security所控制了
/**
* 安全配置类
*/
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
//.authorizeRequests()所有security权限配置的开始
//.antMatchers()拦截什么路径 permitAll()表示任何权限都可以访问 ,直接放行所有
//.anyRequest()任何请求 .authenticated()认证后才能访问
// .and().csrf().disable(); 使csrf拦截失效
http
.authorizeRequests()
.antMatchers("/**").permitAll()
.anyRequest().authenticated()
.and().csrf().disable();
}
}
(3)修改需要使用加密的工程的Application, 配置bean
@Bean
public BCryptPasswordEncoder bcryptPasswordEncoder(){
return new BCryptPasswordEncoder();
}
(4)登陆的加密和解密
在注册的时候进行加密
encoder.encode(user.getPassword())
在登陆的时候解密 (需要用户输入后的密码和,数据库加密后的密码)
/**
* 功能描述:用户登录
*
* @author: hdh
* @date: 2020/2/23 13:00
*/
public User login(User user) {
//判断用户名是否存在
User userLogin = this.userDao.findByMobile(user.getMobile());
//比对加密后的密码和用户输入的密码是否相同
if (userLogin != null && encoder.matches(user.getPassword(), userLogin.getPassword())) {
return userLogin;
}
return null;
}
4.基于JWT的Token认证机制实现
JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用JWT在用 户和服务器之间传递安全可靠的信息。(无状态协议)
一个JWT实际上就是一个字符串,它由三部分组成,头部、载荷与签名。 头部(Header) 头部用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法等。这也可以 被表示成一个JSON对象。
JwtBuilder jwtBuilder = Jwts.builder()
//id
.setId("1")
//用户名
.setSubject("hdh")
//生成token的时间
.setIssuedAt(new Date())
//指明签名算法是HS256算法 hudaihui 加密的盐
.signWith(SignatureAlgorithm.HS256, "hudaihui")
.setExpiration(new Date(new Date().getTime()+15000))
//可以自己添加需要的信息
.claim("role","admin");
iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
5.Java的JJWT实现JW
(1)引入依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
< artifactId>jjwt</artifactId>
<version>0.6.0</version>
</dependency>
(2)使用拦截器和JJWT进行,删除用户权限控制操作,只用admin用户可以进行删除
jjwt产生token和解token的工具类
@ConfigurationProperties("jwt.config")需要配置application.yamml
jwt:
config:
key: hudaihui
ttl: 3600000
@ConfigurationProperties("jwt.config")
public class JwtUtil {
private String key ;
private long ttl ;//一个小时
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public long getTtl() {
return ttl;
}
public void setTtl(long ttl) {
this.ttl = ttl;
}
/**
* 生成JWT
*
* @param id
* @param subject
* @return
*/
public String createJWT(String id, String subject, String roles) {
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
JwtBuilder builder = Jwts.builder().setId(id)
.setSubject(subject)
.setIssuedAt(now)
.signWith(SignatureAlgorithm.HS256, key).claim("roles", roles);
if (ttl > 0) {
builder.setExpiration( new Date( nowMillis + ttl));
}
return builder.compact();
}
/**
* 解析JWT
* @param jwtStr
* @return
*/
public Claims parseJWT(String jwtStr){
return Jwts.parser()
.setSigningKey(key)
.parseClaimsJws(jwtStr)
.getBody();
}
}
(3)springBoot interceptor的编写
需要声明那个是拦截器类
@Configuration
public class InterceptorConfig extends WebMvcConfigurationSupport {
@Autowired
private JwtInterceptor jwtInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtInterceptor).
addPathPatterns("/**").
excludePathPatterns("/**/login");
}
}
拦截器的作用仅仅只是解析所有的请求,把请求头中有token的进行解析
@Component
public class JwtInterceptor implements HandlerInterceptor {
/**
* 功能描述:拦截器作用:负责把请求头中有tocker令牌的进行解析
*
* @author:hdh
* @date: 2020/2/23 18:43
*/
@Autowired
private JwtUtil jwtUtil;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String header = request.getHeader("Authorization");
if (!StringUtils.isEmpty(header)) {
if (header.startsWith("Bearer ")) {
String token = header.substring(7);
try {
Claims claims = jwtUtil.parseJWT(token);
String roles = (String) claims.get("roles");
if (roles != null || roles.equals("admin")) {
request.setAttribute("admin_claims", roles);
}
if (roles != null || roles.equals("user")) {
request.setAttribute("user_claims", roles);
}
} catch (Exception e) {
throw new RuntimeException("令牌过期");
}
}
}
return true;
}
}
(4)用户进行登陆时生成token
/**
* 功能描述:用户登陆
*
* @author:hdh
* @date: 2020/2/23 12:57
*/
@PostMapping("login")
public Result login(@RequestBody User user) {
user = this.userService.login(user);
if (user == null) {
return new Result(false, StatusCode.ERROR, "登陆失败");
}
String token = jwtUtil.createJWT(user.getId(), user.getMobile(), "user");
HashMap<String, Object> map = new HashMap<>();
map.put("token", token);
map.put("roles", "user");
return new Result(true, StatusCode.OK, "登陆成功",map);
}
(5)用户进行删除操作时判断权限是否满足
/**
* 删除
*
* @param id
*/
public void deleteById(String id) {
String admin_claims = (String) request.getAttribute("admin_claims");
if (StringUtils.isEmpty(admin_claims)) {
throw new RuntimeException("权限不足");
}
userDao.deleteById(id);
}