第二天
2.1、登录
2.1.1、传统登录分析
传统的认证是基于Cookie和Session
网络异常,图片无法展示
|
2.1.2、互联网登录
互联网认证是基于Token
2.1.3、整合JWT
2.1.3.4、引入依赖
<dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.10.3</version> </dependency> 复制代码
2.1.3.5、创建工具类
package cn.linstudy.travel.utils; /** * @Description * @Author XiaoLin * @Date 2021/4/10 21:21 */ public class JwtUtil { private static String TOKEN = "XiaoLin";// 私钥 /** * 生成token * @param map //传入payload * @return 返回token */ public static String getToken(Map<String,String> map){ JWTCreator.Builder builder = JWT.create(); map.forEach((k,v)->{ builder.withClaim(k,v); }); Calendar instance = Calendar.getInstance(); // 设置有效期为30分钟 instance.add(Calendar.MINUTE,30); builder.withExpiresAt(instance.getTime()). // 设置签发时间 withIssuedAt(new Date()); return builder.sign(Algorithm.HMAC256(TOKEN)).toString(); } /** * 验证token * @param token * @return */ public static void verify(String token){ try { JWT.require(Algorithm.HMAC256(TOKEN)).build().verify(token); }catch (Exception e){ throw new LogicException("Token校验失败"); } } /** * 获取token中payload * @param token * @return */ public static DecodedJWT getToken(String token){ return JWT.require(Algorithm.HMAC256(TOKEN)).build().verify(token); } } 复制代码
2.1.4、编写接口
@ApiOperation(value = "用户登录") @GetMapping("login") public JsonResult login(UserInfoLoginVO userInfoRegisterVO){ return userInfoService.login(userInfoRegisterVO); } 复制代码
2.1.5、Service接口
/** * @Description: 登录 * @author XiaoLin * @date 2021/4/10 * @Param: [userInfoRegisterVO] * @return cn.linstudy.travel.qo.response.JsonResult */ JsonResult login(UserInfoLoginVO userInfoRegisterVO); 复制代码
2.1.6、实现类
/** * @Description: 登录实现类 * @author XiaoLin * @date 2021/4/10 * @Param: [userInfoRegisterVO] * @return cn.linstudy.travel.qo.response.JsonResult */ @Override public JsonResult login(UserInfoLoginVO userInfoLoginVO) { QueryWrapper wrapper = new QueryWrapper(); wrapper.eq("phone",userInfoLoginVO.getPhone()); UserInfo userInfo = userInfoMapper.selectOne(wrapper); if (userInfo != null){ if (userInfo.getPassword().equals(userInfoLoginVO.getPassword())){ Map<String, String> map = new HashMap<>();//用来存放payload map.put("phone",userInfo.getPhone()); String token = JwtUtil.getToken(map); String key = RedisKeyEnum.ENUM_LOGIN_TOKEN.join(userInfoLoginVO.getPhone()); String LoginToken = userInfoRedisService.getValue(key); if (LoginToken != null){ userInfoRedisService.resetTime(key); } // 将token放入redis中 userInfoRedisService.setLoginToekn(key,token); Map<String,Object> resultMap = new HashMap<>(); resultMap.put("token",token); resultMap.put("user",userInfo); return JsonResult.success(resultMap); }else { throw new LogicException("密码错误"); } } throw new LogicException("手机号未注册"); } 复制代码
2.1.7、测试Token是否签发
2.1.8、前端将Token设置到Cookie中
if (result.code == 200) { var map = result.data; var token = map.token; //后续后端获取当前登录用户信息 console.log(token); var user = map.user; console.log(user)//前端页面需要显示用户信息 //sessionStorage 客户端技术可以在浏览器窗口存储数据, 一但关闭窗口, // 数据就没了, 是如果多个窗口, 数据无法共享 //localStorage 客户端技术可以在浏览器窗口存储数据, 数据操作是永久 //cookie 客户端技术可以在浏览器窗口存储数据, 特点有时效性 //参数1:cookie的key值, 参数2: cookie的value值, 参数3: 有效时间, 单位天 Cookies.set('user', JSON.stringify(user), {expires: 1 / 48, path: '/'}); Cookies.set('token', token, {expires: 1 / 48, path: '/'}); document.referrer //上一个请求路径 var url = document.referrer ? document.referrer : "/"; if (url.indexOf("regist.html") > -1 || url.indexOf("login.html") > -1) { url = "/"; } window.location.href = url } else { popup(result.msg); } 复制代码
2.1.9、测试Cookie是否赋值
2.2、权限
我们使用拦截器去配置权限无法做到细粒度的设置,所以我们可以使用自定义注解来控制权限。
2.2.1、自定义注解
package cn.linstudy.travel.annotation; /** * @Description 登录拦截注解,如果请求方法贴有该注解,表示该方法需要进行登录检查 * @Author XiaoLin * @Date 2021/4/11 15:01 */ @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface PassLogin { } 复制代码
2.2.2、书写拦截器
package cn.linstudy.travel.interceptor; /** * @Description 拦截器 * @Author XiaoLin * @Date 2021/4/11 14:03 */ public class JwtInterceptor implements HandlerInterceptor { @Autowired UserInfoService userInfoService; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if( !(handler instanceof HandlerMethod)){ return true; } //1:判断当前请求对应请求映射方法是否贴@RequireLogin标签 HandlerMethod method = (HandlerMethod) handler; String token = request.getHeader("token"); if (method.hasMethodAnnotation(PassLogin.class)){ return true; }else { if (token == null) { response.setContentType("application/json;charset=utf-8"); response.getWriter().write(JSON.toJSONString(JsonResult.noLogin())); return false; }else { //否则就是说明需要登录的 //获得签发对象(用户id) String userId = JwtUtil.getAudience(token); // 去数据库查询是否有这个用户,验证用户是否存在 UserInfo userInfo = userInfoService.checkUserById(Long.parseLong(userId)); // 如果为空,说明用户不存在 if (userInfo == null) { throw new LogicException("用户不存在"); } // 验证Token是否合法 JwtUtil.verify(token, userId); return true; } } } } 复制代码
2.2.3、配置拦截器
package cn.linstudy.travel.config; /** * @Description 配置拦截器 * @Author XiaoLin * @Date 2021/4/11 14:02 */ @Configuration public class WebConfig implements WebMvcConfigurer { // 将jwtInterceptor拦截器的实例对象放入Bean容器 @Bean public JwtInterceptor jwtInterceptor(){ return new JwtInterceptor(); } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(jwtInterceptor()) .addPathPatterns("/**") .excludePathPatterns("/users/login") .excludePathPatterns("/users/register") .excludePathPatterns("/users/sendVerifyCode") .excludePathPatterns("/users/checkPhone") .excludePathPatterns("/swagger-resources/**") .excludePathPatterns("/v2/api-docs/**") .excludePathPatterns("/swagger-resources/configuration/security/**") .excludePathPatterns("/webjars/**") .excludePathPatterns("/swagger-ui.html") .excludePathPatterns("/error") .excludePathPatterns("/swagger-resources/configuration/ui/**"); } }