目录
第二步:自定义注解AuthAccess 在需要放开的地方把他放开 不对放开的路径拦截
第六步:修改service 在service里添加生成token的方法
登录完之后获得了token,token存到了浏览器里面,然后在以后的每一次的数据请求都会拿token才会向后台发送去获取数据如果说在没有登录的情况下直接访问后台的接口是会报错的是不允许访问的,token像一把钥匙一样来保护后台的数据。例子:有些数据是比较敏感的,比如后台的订单数据是比较敏感的不能让所有人都看到所以需要一把锁把数据锁上,token就是锁的钥匙,只有登录后才会拿到token,拿到钥匙(token)之后就可以访问后台的所有数据了.JWT是token实现的一种方式。
本质是一个拦截器:把所有访问过来的请求进行拦截,拦截下来后检查有没有token,有就放行。没有就报错
第一步:在pom.xml文件中添加jwt依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.3.0</version>
</dependency>
添加依赖后点击maven刷新
第二步:自定义注解AuthAccess 在需要放开的地方把他放开 不对放开的路径拦截
在common下创建一个annotation,用法:在register方法上加这个注解,写注解就代替了在拦截器里面写路径
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AuthAccess {
}
第三步:在coommon包下定义一个拦截规则
public class JwtInterceptor implements HandlerInterceptor {
@Resource
private UserMapper userMapper;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
//1、首先用request拿到header(请求头)里的token参数
String token = request.getHeader("token");
//2、拿到token参数以后进行校验,看他有没有.getparameter获取到的是url参数比如:xxx?token=... 可以用1和2两种方式接受参数一个是头一个是url参数,两个里面有一个获取到了token参数就认为他是有这个参数的
if (StrUtil.isBlank(token)) {
token = request.getParameter("token");
}
// 如果不是映射到方法直接通过 配合自定义注解使用
//if (handler instanceof HandlerMethod) {
//AuthAccess annotation = ((HandlerMethod) handler).getMethodAnnotation(AuthAccess.class);
//if (annotation != null) {
//return true;
//}
//}
//3、如果请求头和参数里面都没有则抛出异常返回信息“请登录” 执行认证
if (StrUtil.isBlank(token)) {
throw new ServiceException("401", "请登录");
}
//4、如果有token的话 token里面存储的是字符串所以定义的时候是string getAudience()可以存储信息 获取 token 中的 userid JWT.decode(token)解码解码完成之后拿到getAudience()里面的第一个数据get(0)
String userId;
try {
userId = JWT.decode(token).getAudience().get(0);
} catch (JWTDecodeException j) {
throw new ServiceException("401", "请登录");
}
//5、 根据token中的userid查询数据库进行信息校验Integer.valueOf(userId)把字符串转化为数字然后查询数据库
User user = userMapper.selectById(Integer.valueOf(userId));
if (user == null) {
throw new ServiceException("401", "请登录");
}
//6、 用户密码加签验证:通过user拿到密码 jwtVerifier是一个验证器,通过用户密码加密之后生成的一个验证器 通过验证器的verify方法继续验证token,如果验证通过return true,如果验证失败则跑一个异常
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getPassword())).build();
try {
jwtVerifier.verify(token); // 验证token
} catch (JWTVerificationException e) {
throw new ServiceException("401", "请登录");
}
return true;
}
}
第四步:在common包下定义拦截器
//1、继承WebMvcConfigurationSupport
@Configuration//表示这是一个配置类
public class InterceptorConfig extends WebMvcConfigurationSupport {
//2、addInterceptors方法是继承WebMvcConfigurationSupport需要重写一个拦截器的规则jwtInterceptor()他通过@Bean的方法将对象注入到spring容器里面
@Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtInterceptor())// 配置jwt的拦截器规则
.addPathPatterns("/**") //addPathPatterns("/**")/**表示拦截所有的请求路径
.excludePathPatterns("/login","/register"); //应该拦截除了登录,注册请求以外的所有路径
super.addInterceptors(registry);
}
@Bean
public JwtInterceptor jwtInterceptor() {
return new JwtInterceptor();
}
}
在InterceptorConfig里拦截JwtInterceptor里校验
第五步:生成token给前端,定义工具类
前端登录完成之后后台需要把token返回给前端,在utils包下定义TokenUtils类
@Component
public class TokenUtils {
//1、定义一个静态mapper的原因,因为下面有static方法,在static方法里面像使用mapper那么mapper必须是static修饰的(静态的方法只能访问静态的变量)
private static UserMapper staticUserMapper;
@Resource
UserMapper userMapper;
@PostConstruct
public void setUserService() {
staticUserMapper = userMapper;
}
/**
* 2、生成token
*
* @return
*/
public static String genToken(String userId, String sign) {
return JWT.create().withAudience(userId) // 将 user id 保存到 token 里面,作为载荷
.withExpiresAt(DateUtil.offsetHour(new Date(), 2)) // 2小时后token过期 withExpiresAt设置过期时间。offsetHour(new Date(), 2)表示当前日期new Date()往后偏移两个小时,他返回的是一个时间
.sign(Algorithm.HMAC256(sign)); // 以 password 作为 token 的密钥.sign(Algorithm.HMAC256(sign))进行加密 sign是传过来的参数,传什么参数生成什么类型的验证器
}
/**
* 3、获取当前登录的用户信息
*只要当前请求有token我就可以通过token去拿到当前请求的用户的所有的信息(是从数据库里面查出来的 )
* @return user对象
*/
public static User getCurrentUser() {
try {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String token = request.getHeader("token");
if (StrUtil.isNotBlank(token)) {
String userId = JWT.decode(token).getAudience().get(0);
return staticUserMapper.selectById(Integer.valueOf(userId));
}
} catch (Exception e) {
return null;
}
return null;
}
}
第六步:修改service 在service里添加生成token的方法
//webcontroller
@PostMapping("/login")
public Result login(@RequestBody User user){
if(StrUtil.isBlank(user.getUsername()) || StrUtil.isBlank(user.getPassword())){
return Result.error("数据输入不合法");
}
user = userService.login(user);
return Result.success(user);
}
//userService
public User login(User user){
//根据用户名查询数据库的用户信息
User dbuser = userMapper.selectUserByUsername(user.getUsername());
...
//生成token,在user实体类里面加一个属性token, public String token ,通过user将token传递出去
String token = TokenUtils.genToken(dbuser.getId().toString(),dbuser.password());
// String token = TokenUtils.genToken(user.getId().toString(),user.password());不能是user.getId()因为user是前端传递过来的,只有name 和 password属性没有id属性 ,所有用了user会出现空指针异常
dbuser.setToken(token);
return dbuser;
}
现在前端已经有了token,现在就需要在所有的接口里面加上token。这个token就是登录接口返回的token在每次的请求的时候,都会在请求头带上这个token 作为验证信息
let user = JSON.parse(localStorage.getItem("honey-user") || '{}')
config.headers['token'] = user.token // 设置请求头
以后所有的接口的请求数据除了登录注册之外的所有的请求数据都需要加token来作为钥匙去发给后台让后台进行校验,网页应该设置安全退出,当用户退出的时候删除浏览器缓存的token信息