前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >实战系列之注入用户信息的几种方法

实战系列之注入用户信息的几种方法

作者头像
山行AI
发布2019-06-28 11:15:52
4840
发布2019-06-28 11:15:52
举报
文章被收录于专栏:山行AI

web项目中一般用户信息是通过加密处理放在cookie中的,如果每个需要用户信息的接口都要去cookie中获取然后解密得到用户信息的话就比较麻烦,这里介绍的就是如何避免这种麻烦。

1. 过滤器方式

在过滤器中包装httpServletRequest:

代码语言:javascript
复制
public class CsrfFilter implements Filter {     @Override        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)                throws IOException, ServletException {                 HttpServletRequest httpServletRequest = (HttpServletRequest) request;                 Cookie[] cookies = httpServletRequest.getCookies();                 Long userId = JwtUtils.getUserIdFromCookie(cookies);                HttpServletResponse httpServletResponse = (HttpServletResponse) response;                 chain.doFilter(new MyHttpServletRequest(httpServletRequest,userId), httpServletResponse);        }}

对应的MyHttpServletRequest中的定义如下:

代码语言:javascript
复制
public class MyHttpServletRequestWrapper extends HttpServletRequestWrapper {    private Long userId;    public MyHttpServletRequestWrapper(HttpServletRequest request, Long userId) {        super(request);        this.userId = userId;    }    public MyHttpServletRequestWrapper(HttpServletRequest request) {        super(request);    }    @Override    public HttpSession getSession() {        return super.getSession(false);    }    /**     * 通过request.getHeader获取     * @param name     * @return     */    @Override    public String getHeader(String name) {        if (name.equals("userId")){            return userId.toString();        }        return super.getHeader(name);    }    /**     * 与getParameterNames处理后可以直接在controller层直接通过@RequestParam可直接写在接口参数列表中注入。     * @param name     * @return     */    @Override    public String[] getParameterValues(String name) {        if (name.equals("userId")){            return new String[]{userId.toString()};        }        return super.getParameterValues(name);    }    @Override    public Enumeration<String> getParameterNames() {        Set<String> params = new LinkedHashSet<>();        params.add("userId");        Enumeration<String> names = super.getParameterNames();        while (names.hasMoreElements()){            params.add(names.nextElement());        }        return Collections.enumeration(params);    }    @Override    public ServletInputStream getInputStream() throws IOException {        final ByteArrayInputStream bais = new ByteArrayInputStream(                    inputHandlers(super.getInputStream()).getBytes());            return new ServletInputStream() {                @Override                public int read() throws IOException {                    return bais.read();                }                @Override                public boolean isFinished() {                    return false;                }                @Override                public boolean isReady() {                    return true;                }                @Override                public void setReadListener(ReadListener readListener) {                }            };    }    /**     * 针对json请求     * @param servletInputStream     * @return     */    public String inputHandlers(ServletInputStream servletInputStream) {        StringBuilder sb = new StringBuilder();        BufferedReader reader = null;        try {            reader = new BufferedReader(new InputStreamReader(servletInputStream, Charset.forName("UTF-8")));            String line = "";            while ((line = reader.readLine()) != null) {                sb.append(line);            }        } catch (IOException e) {            e.printStackTrace();        } finally {            if (servletInputStream != null) {                try {                    servletInputStream.close();                } catch (IOException e) {                    e.printStackTrace();                }            }            if (reader != null) {                try {                    reader.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }        JSONObject jsonObject = JSONObject.parseObject(sb.toString());        jsonObject.put("userId",userId);        return jsonObject.toJSONString();    }}

这样无论get还是post还是json请求,在controller层就能通过接口中参数注入的方式都能直接获取用户信息了。

2. 注解加上拦截器加上HandlerMethodArgumentResolver的方式

  • 注解如下:
代码语言:javascript
复制
//需要注入用户信息的参数级别的注解@Target(ElementType.PARAMETER) // 可用在方法的参数上@Retention(RetentionPolicy.RUNTIME) // 运行时有效public @interface CurrentUser {}//需要用户登录的方法级别的注解@Documented@Inherited@Target({ElementType.METHOD,ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)public @interface AuthPassport {    boolean validate() default true;    /**     * true时,代表接口可登录可不登录     * @return     */    boolean noNeed() default false;}
  • 拦截器中主要是拦截AuthPassport注解请求,并从cookie解析用户信息放入request中。
  • resolver中代码如下:
代码语言:javascript
复制
public class CurrentUserMethodArgumentResolver implements HandlerMethodArgumentResolver {    @Override    public boolean supportsParameter(MethodParameter parameter) {        return parameter.getParameterType().isAssignableFrom(UserBaseTo.class)                && parameter.hasParameterAnnotation(CurrentUser.class);    }    @Override    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,                                  NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {        UserBaseTo user = null;        if (parameter.getParameterType().isAssignableFrom(UserBaseTo.class)                && parameter.hasParameterAnnotation(CurrentUser.class)) {            user = (UserBaseTo) webRequest.getAttribute(BaseGlobalConstants.CURRENT_USER, RequestAttributes.SCOPE_REQUEST);        }        return user;    }}

这样在controller中就能以下面这种方式获取用户信息了:

代码语言:javascript
复制
@RequestMapping(value = "/query", method = RequestMethod.GET)    @BrowseRecord()    @AuthPassport(noNeed = true)    public ResultVo<Object> get(@PathVariable String origin, @ApiIgnore @CurrentUser UserBaseTo userBaseTo, Long contentId,Integer isFree) {    }

3. threadLocal方式

这种方式就不需要多讲了,主要通过在请求进入时将用户信息放入当前io线程的threadLocal中携带过来,然后在下层就能从threadLocal中去获取了。在一些使用了线程池隔离技术来处理请求的场景,这种方式是行不通的, 比如使用了hystrix。在使用线程池的场景中一个要求就是,每个线程处理完之后,需要清空threadLocal中的信息以便线程复用。

以前在做dubbo + hystrix + zipkin时,配置dubbo的filter时,如果将hystrixFilter配置在zipkinFilter前面就会有问题,因为zipkin中的统计信息是通过threadLocal来传递的。

如果想解决这个问题,可以看下阿里开源的一个组件:https://github.com/alibaba/transmittable-thread-local。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-06-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 开发架构二三事 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. 过滤器方式
  • 2. 注解加上拦截器加上HandlerMethodArgumentResolver的方式
  • 3. threadLocal方式
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档