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); }