- 通过前端访问成功,直接访问后端接口失败。
- 思考的过程、问题的解决
- Session和sessionStorage
通过前端访问成功,直接访问后端接口失败。
做黑马点评的使用Redis代替Session实现短信登录的功能时,遇到了一个问题:
就是我设计好代码后,登陆后应该会有一个token返回给前端,然后后面的访问前端就会带着这个token获取到redis中的用户信息不用重复登录了。但是当我调试的时候我发现。我在登录好的界面里刷新,它会有http://127.0.0.1:8080/api/user/me的请求发出去,并且正确获得结果。
但是当我在浏览器中更直接输入http://127.0.0.1:8080/api/user/me进行访问的时候,它会返回401错误代码。
思考的过程、问题的解决
这个401是我设计的代码返回的,拦截器中如果找不到User信息的话就会返回401,于是我顺着请求找进去。
首先,它会通过第一层拦截器RefreshTokenInterceptor,拦截所有访问,在这里它会有String token = request.getHeader(“authorization”);获取请求头里的token。
public class RefreshTokenInterceptor implements HandlerInterceptor {private StringRedisTemplate stringRedisTemplate;public RefreshTokenInterceptor(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {String token = request.getHeader("authorization");if(StrUtil.isBlank(token)){return true;}Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries(LOGIN_USER_KEY + token);if(entries.isEmpty()){return true;}UserDTO userDTO = BeanUtil.fillBeanWithMap(entries, new UserDTO(), false);UserHolder.saveUser(userDTO);stringRedisTemplate.expire(LOGIN_USER_KEY+token, LOGIN_USER_TTL, TimeUnit.MINUTES);//2.有用户,则放行return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {UserHolder.removeUser();}
}
然后,它又会进入第二层拦截器LoginInterceptor,它会拦截所有需要用户登陆后才能访问的页面。401就是在这里被返回出去的。
既然Redis中的User信息是保存好的(我看了一眼Redis),那么只会是这里出错了,token应该返回的是null(后来我编写了打印语句证实token确实是null)。
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if(UserHolder.getUser() == null){response.setStatus(401);return false;}//2.有用户,则放行return true;}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {UserHolder.removeUser();}
}
这就有点意思了,然后我又去开发者模式F12里去找数据,终于在我从成功访问的页面也就是下面这个页面看到了会话存储空间中保存着一个token值。
而直接访问目标接口的时候在会话存储里什么都没有。这是为什么。
于是他们的问题就是出现在了一个有会话存储,一个没有会话存储。
那么会话存储是怎么一回事呢?DeepSeek是这么说的:
涉及到前端,突然我又想到两者的区别还有就是一个是访问的前端页面然后发出的http://127.0.0.1:8080/api/user/me请求,还有一个是直接发出的http://127.0.0.1:8080/api/user/me请求。那么问题有很大的可能出现在这里。经过查阅前端代码,发现前端确实有一个登录跳转,将token保存到了
sessionStorage中,所以对sessionStorage的访问肯定也必须要经过前端才行。而单独地访问一个后端接口是不会携带sessionStorage的。
至此谜团被解开。
Session和sessionStorage
我又去了解了一下服务端session和客户端sessionStorage的区别,因为他们都有session,我会分不清。发现了session这个词不是服务端独有的,它的翻译是会话,计算机网络七层架构中也有会话层这个说法,所以服务端的session我之前一直以为它是服务端特有的,这个想法是错误的,session就是会话的意思。