1. BCrypt密码加密
密码应该通过哈希算法进行加密,有很多标准算法如SHA或者MD5,结合salt(盐)是一个不错的选择。Spring Security提供了BCryptPasswordEncoder类,实现Spring的PasswordEncoder接口使用BCrypt强哈希方法来加密密码。BCrypt强哈希方法每次加密结果都不一样。
导包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
配置
// 安全配置类
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
// authorizeRequests所有security全注解配置实现的开端,表示开始说明需要的权限
// 需要的权限分两部分,第一部分时拦截的路径,第二部分是访问该路径需要的权限
// anyMatches表示拦截什么路径,permitAll任何路径都可以访问
// anyRequest任何的请求,authenticated认证后才能访问
// and().csrf().disable(),固定写法,表示csrf拦截失效
@Overide
protected void configure(HttpSecurity http) throws Exception{
http.authorizeRequests()
.anyMatches("/**").permitAll()
.anyRequest().authenticated()
.and().csrf().disable();
}
}
使用
// 启动类注入BCryptPasswordEncoder
@Bean
public BCryptPasswordEncoder(){
return new BCryptPasswordEncoder();
}
// 注入后直接调用即可
@Autowired
private BCryptPasswordEncoder encoder;
// 加密
encoder.encode("password");
// 密码比较
encoder.matches("用户输入密码", "数据库密码");
2. 常见的认证机制
2.1 HTTP Basic Auth
2.2 Cookie Auth
Cookie认证机制就是为一次请求认证在服务端创建一个Session对象,同时在客户端浏览器创建了一个Cookie对象;通过客户端带上来的Cookie对象与服务器端的session对象匹配来实现状态管理的。默认的,当我们关闭浏览器的时候,cookie会被删除。但可以通过cookie的expire time使cookie在一定时间内有效。
2.3 OAuth
2.4 Token Auth
使用基于Token的身份验证方法,在服务端不需要存储用户的登录记录,流程如下:
1)客户端使用用户名和密码请求登录
2)服务端收到请求,去验证用户名与密码
3)验证成功后,服务端会签发一个Token,再把这个Token发给客户端
4)客户端收到Token后把它存储起来,比如放在Cookie里
5)客户端每次请求服务端资源时都需要带着服务端签发的Token
6)服务端收到请求,然后去验证客户端请求里面带着的Token,如果验证成功就向客户端返回请求的数据】
Token机制优点:
1)支持跨域访问:Cookie不允许跨域访问
2)无状态:Token机制在服务端不需要存储session信息
3)更适用CDN
4)去耦
5)更适用于移动应用
6)CSRF,因为不再依赖Cookie,所以不需要考虑对CSRF(跨站请求伪造)的防范
3. 基于JWT的Token认证机制
JSON Web Token是一个非常轻巧的规范,这个规范允许我们使用JWT在用户和服务器之间传递安全可靠的信息。
一个JWT实际上就是一个字符串,由三部分组成——头部(Header)、载荷(playload)与签证(signature)。
头部:用于描述关于该JWT的最基本的信息,例如其类型以及签名所用的算法,如{"type": "JWT", "alg": "HS256"}
载荷:存放有效信息的地方,如 {"sub": "1234567890", "name": "Tom"}
签证:由三部分组成header(base64为后的),playload(base64位后的),secret(盐)
注意:secret是保存在服务端的,jwt的签发生成也是在服务端的,secret就是用来进行jwt的签发和jwt的验证,所以,它就是你服务端的私钥,任何时候不应流露出来。
4. Java的JJWT实现JWT
JJWT是一个提供端到端的JWT创建和验证的Java库。
导包
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.6.0</version>
</dependency>
使用
// 生成JWT 设置过期时间,自定义属性等
JwtBuilder jwtBuilder = Jwts.builder()
.setId("1")
.setSubject("Tom")
.setIssuedAt(new Date())
.signWith(SignatureAlgorithm.JS256, "salt")
.setExpiration(new Date(new Date().getTime() + 60 * 1000))
.claims("role", "admin");
System.out.println(jwtBuilder.compact());
// 解析JWT
Claims claims = JWTs.parser().setSigningKey("salt")
.parseClaimsJws("加密后的Token")
.getBody();
System.out.println("用户名id:" + claims.getId());
5. 拦截器实现用户权限控制
拦截器
@Component
public class JwtInterceptor implements HandlerInterceptor{
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throw Exception{
System.out.println("经过了拦截器");
// 无论如何都放行,具体能不能操作还是在具体的操作中去判断
// 拦截器只是负责把请求头中包含token的令牌进行一个解析验证
String header = request.getHeader("Authorization"); // Authorization这个是前后端约定好的需要添加头信息,token放请求头更安全
if(header != null && !"".equals(header)){
String token = header;
try{
Claims claim = jwtUtil.parseJWT(token);
String roles = (String)claim.get("roles");
if(roles != null && roles .equals("admin")){
request.setAttribute("claims_admin", token);
}
}catch(Exception e){
throw new RuntimeException("权限不足!");
}
}
return true;
}
}
拦截配置
@Configuration
public class InterceptorConfig extends WebMvcConfigurationSupport{
@Autowired
private JwtInterceptor jwtInterceptor;
protected void addInterceptors(InterceptorRegistry registry){
// 注册拦截器要声明拦截器对象和要拦截的请求
register.addInterceptors(jwtInterceptor)
.addPathPatterns("/**")
.excludePathPatterns("/**/login/**");
}
}