Token认证,如何快速方便获取用户信息

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Token认证,如何快速方便获取用户信息相关的知识,希望对你有一定的参考价值。

背景
我们有一个Web项目,这个项目提供了很多的Rest API。也做了权限控制,访问API的请求必须要带上事先认证后获取的Token才可以。
认证的话就在Filter中进行的,会获取请求的Token进行验证,如果成功了可以得到Token中的用户信息,本文的核心就是讲解如何将用户信息(用户ID)优雅的传递给API接口(Controller)。
方式一(很挫)
我们在Filter中进行了统一拦截,在Controller中获取用户ID的话,仍然可以再次解析一遍Token获取用户ID

1.  @GetMapping("/hello")
2.  public String test(HttpServletRequest request) {
3.      String token = request.getHeader("token");
4.      JWTResult result = JWTUtils.checkToken(token);
5.      Long userId = result.getUserId();
6.  }

方式二(优雅)

方式一需要重新解析一遍Token, 浪费资源。我们可以直接将Filter中解析好了的用户ID直接通过Header传递给接口啊。

1.  @Override
2.  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
3.              throws IOException, ServletException {
4.      HttpServletRequest httpRequest = (HttpServletRequest) request;
5.      HttpServletResponse httpResponse = (HttpServletResponse) response;
6.      String token = request.getHeader("token");
7.      JWTResult result = JWTUtils.checkToken(token);
8.      Long userId = result.getUserId();
9.      HttpServletRequestWrapper requestWrapper = new HttpServletRequestWrapper(httpRequest) {
10.         @Override
11.         public String getHeader(String name) {
12.             if (name.equals("loginUserId")) {
13.                 return userId .toString();
14.             }
15.             return super.getHeader(name);
16.         }
17.     };
18.     chain.doFilter(requestWrapper, httpResponse);
19. }
接口中直接从Header中获取解析好了的用户ID:
1.  @GetMapping("/hello")
2.  public String save2(HttpServletRequest request) {
3.      Long userId = Long.parseLong(request.getHeader("loginUserId")); 
4.  }

方式三(很优雅)
通过Header传递确实很方便,但如果你有代码洁癖的话总会觉得怪怪的,能不能不用Header方式,比如说我就在方法上定义一个loginUserId的参数,你给我直接注入进来,这个有点意思哈,下面我们来实现下:
GET参数方式
在Filter中追加参数:

1.  @Override
2.  public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
3.              throws IOException, ServletException {
4.      HttpServletRequest httpRequest = (HttpServletRequest) request;
5.      HttpServletResponse httpResponse = (HttpServletResponse) response;
6.      String token = request.getHeader("token");
7.      JWTResult result = JWTUtils.checkToken(token);
8.      Long userId = result.getUserId();
9.      HttpServletRequestWrapper requestWrapper = new HttpServletRequestWrapper(httpRequest) {
10.             @Override
11.             public String[] getParameterValues(String name) {
12.                 if (name.equals("loginUserId")) {
13.                     return new String[] { userId .toString() };
14.                 }
15.                 return super.getParameterValues(name);
16.             }
17.             @Override
18.             public Enumeration<String> getParameterNames() {
19.                 Set<String> paramNames = new LinkedHashSet<>();
20.                 paramNames.add("loginUserId");
21.                 Enumeration<String> names =  super.getParameterNames();
22.                 while(names.hasMoreElements()) {
23.                     paramNames.add(names.nextElement());
24.                 }
25.                 return Collections.enumeration(paramNames);
26.             }
27.     };
28.     chain.doFilter(requestWrapper, httpResponse);
29. }

接口中直接填写参数即可获取:

1.  @GetMapping("/hello")
2.  public String save2(String name, Long loginUserId) {
3.      // loginUserId 就是Filter中追加的值
4.  }

对于post请求,也可以用这种方式:

1.  @PostMapping("/hello")
2.  public String save2(User user, Long loginUserId) {
3.  
4.  }

可是往往我们在用post请求的时候,要么就是表单提交,要么就是json体的方式提交,一般不会使用get方式参数,这也就意味着这个loginUserId我们需要注入到对象中:
先创建一个参数实体类:

1.  public class User {
2.  
3.      private String name;
4.  
5.      private Long loginUserId;
6.  }

先模拟表单提交的方式,看看行不行:

1.  @PostMapping("/hello")
2.  public User save2(User user) {
3.      return user;
4.  }

用PostMan测试一下,表单方式是直接支持的:
技术图片
再次试下Json提交方式:

1.  @PostMapping("/hello")
2.  public User save2(@RequestBody User user) {
3.      return user;
4.  }

看下图,失败了,得重新想办法实现下
技术图片
只需要在HttpServletRequestWrapper中重新对提交的内容进行修改即可:

1.  @Override
2.  public ServletInputStream getInputStream() throws IOException {
3.      byte[] requestBody = new byte[0];
4.      try {
5.          requestBody = StreamUtils.copyToByteArray(request.getInputStream());
6.          Map map = JsonUtils.toBean(Map.class, new String(requestBody));
7.          map.put("loginUserId", loginUserId);
8.          requestBody = JsonUtils.toJson(map).getBytes();
9.      } catch (IOException e) {
10.         throw new RuntimeException(e);
11.     }
12.     final ByteArrayInputStream bais = new ByteArrayInputStream(requestBody);
13.     return new ServletInputStream() {
14.          @Override
15.           public int read() throws IOException {
16.                return bais.read();
17.           }
18. 
19.           @Override
20.            public boolean isFinished() {
21.                return false;
22.            }
23. 
24.            @Override
25.            public boolean isReady() {
26.                 return true;
27.            }
28. 
29.            @Override
30.             public void setReadListener(ReadListener listener) {
31. 
32.             }
33.     };
34. }

到此为止,我们就可以直接将Token解析的用户ID直接注入到参数中了,不用去Header中获取,是不是很方便。
欢迎加入我的知识星球,一起交流技术,免费学习猿天地的课程(http://cxytiandi.com/course
PS:目前星球中正在星主的带领下组队学习Spring Cloud,等你哦!

技术图片
尹吉欢
我不差钱啊
钟意作者

以上是关于Token认证,如何快速方便获取用户信息的主要内容,如果未能解决你的问题,请参考以下文章

再谈Token认证,如何快速方便获取用户信息

oauth2认证后返回#怎么获取accesstoken?

jwt 无状态分布式授权

jwt的token怎么生存的

开箱即用 - jwt 无状态分布式授权

Express实战 - 应用案例- realworld-API - 路由设计 - mongoose - 数据验证 - 密码加密 - 登录接口 - 身份认证 - token - 增删改查API(代码片段