jq获取id的名称_再谈Token认证,如何快速方便获取用户信息

9948a9f30232b9e3888390141b7e9598.png

前面我写了一篇《Token认证,如何快速方便获取用户信息》的文章,引起了各位读者的积极参与,除了文章中我提出的三种方式,各位读者大佬们也贡献了其他多种实现方式。

今天决定基于大家提供的思路再写一篇文章,主要是有读者留言说想要知道其他的实现方式,没办法,只能自己先研究下,然后分享出来,我就是这么宠读者,哈哈。

9537f968a8e8cd9062103d1e43b7cbcd.png

12216c2c48e2b7f20ae936fd819aae1d.png

1797bb3e15884a9e0ef7998a3480fb68.png

e3eb98fe96b4ae844f53454e5e7a8dbf.png

总结起来就是ThreadLocal,AOP,HandlerMethodArgumentResolver这三种方式,当然这些都是别人提供的方案,也许他们在实际工作中使用过,我本人是没接触过这块,但是我临时去实现了一下,不知道是不是跟各位留言中的实现一致,但是效果肯定是实现了的。

仅供大家参考,写的不好不要嘲笑我哈。

ThreadLocal

如果用ThreadLocal的话也挺简单的,在过滤器中解析Token之后将用户ID set 到ThreadLocal中,在Controller中get就可以获取到了,如下:

// 定义

public static ThreadLocal<Long> loginUserThreadLocal = new ThreadLocal<>();

// 设置

loginUserThreadLocal.set(userId);

// 获取

loginUserThreadLocal.get()

需要注意的是:如果你的Controller方法用了@HystrixCommand注解,意味着这个方法执行的线程就是hystrix的线程了,过滤器中是容器的线程,这个时候用ThreadLocal是获取不到值的,这就涉及到了一个跨线程传递的问题了,我之前也有写过类似的文章,用的是transmittable-thread-local这个框架来解决的。

文章可以参考这2篇:

http://cxytiandi.com/blog/detail/13331

http://cxytiandi.com/blog/detail/18782

AOP

还有一位朋友提到了ThreadLocal+AOP的方式,我想他的意思应该是从Filter中解析出用户ID, 然后存储到ThreadLocal中,在AOP中获取ThreadLocal中的用户ID, 然后注入到参数中,这样感觉整个操作流程都变长了。

我们还是按照这个思路来实现下吧:

我们直接在切面中对参数进行修改,最简单的方式是直接获取参数列表,然后修改,比如:

Object[] args = joinPoint.getArgs();

args[1] = 用户ID;

return joinPoint.proceed(args);

这段代码很明显不好,因为通过下标的方式去修改参数,也就意味着所有的接口方法都得将参数放在固定的位置,如下:

@GetMapping("/article/callHello")

public String callHello(String name, Long userId) {

// userId 可以获取到值

}

正如前面有位朋友提到的,可以自定义注解来标识,除了普通的参数,还有实体类这种参数,所以自定义注解是一个比较好的方式。如果不自定义注解,那么就是基于约定的方式,约定好变量名也行,前面我们讲的都是基于约定来的。

我们基于约定好的变量名来讲解,反射获取方法名不是很方便,当jdk1.8中其实已经支持了,为了简化,我们可以用注解的方式来获取参数名称,当然这个注解你可以自定义,也可以用一些现成的,比如@RequestParam:

@GetMapping("/article/callHello")

public String callHello(String name, @RequestParam(name="userId",required=false)Long userId) {

}

这样我们在切面中可以获取当前访问方法中的参数注解列表,然后获取到对应的名称进行匹配,再进行参数值的替换:

Object[] args = joinPoint.getArgs();

Object target = joinPoint.getTarget();

// 方法名

String methodName = joinPoint.getSignature().getName();

Class> clz = target.getClass();

Method[] methods = clz.getDeclaredMethods();

for (Method method : methods) {

// 匹配当前访问的方法

if (methodName.equals(method.getName())) {

// 获取参数注解

Annotation[][] parameterAnnotaions = method.getParameterAnnotations();

for (int i = 0; i < parameterAnnotaions.length; i++) {

Annotation[] oneParameterAnnotaions = parameterAnnotaions[i];

for (int j = 0; j < oneParameterAnnotaions.length; j++) {

// 匹配注解

if (oneParameterAnnotaions[j].annotationType() == RequestParam.class) {

RequestParam param = (RequestParam) oneParameterAnnotaions[j];

// 匹配参数名称

if (param.name().equals("userId")) {

// 设置参数值

args[i] = 用户ID;

}

}

}

}

}

}

result = joinPoint.proceed(args);

这边需要注意的是我这边比对当前方法是直接通过方法名去对比的,会存在一个问题就是如果有相同名称的方法就会出问题,建议大家还是要加上参数的对比,获取直接根据class和方法名称和参数列表进行反射动态获取。这边只为了演示跟大家说明下,我还是建议用过滤器的方式实现,更简单点。

HandlerMethodArgumentResolver

SpringMVC提供了HandlerMethodArgumentResolver接口来处理我们的自定义参数的解析。 我们可以利用这个功能将用户登录的信息绑定到参数中。

最好的方式是单独加一个用户信息实体类,直接作为一个参数进行注入,使用也方便,首先我们定义一个参数类:

@Data

public class LoginUser {

private Long userId;

}

然后定义一个注解,用来标识是否要注入用户参数信息:

@Target({ElementType.PARAMETER})

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface LoginUserAnno {

}

实现HandlerMethodArgumentResolver接口,自定义参数注入的逻辑:

public class LoginUserMethodArgumentResolver implements HandlerMethodArgumentResolver {

@Override

public boolean supportsParameter(MethodParameter parameter) {

return parameter.hasParameterAnnotation(LoginUserAnno.class);

}

@Override

public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,

NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {

LoginUser user = new LoginUser();

user.setUserId(用户ID);

return user;

}

}

配置HandlerMethodArgumentResolver:

@Configuration

public class Config implements WebMvcConfigurer {

@Override

public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {

argumentResolvers.add(new LoginUserMethodArgumentResolver());

}

}

使用的话就很简单了,如下:

@PostMapping("/add")

public User add(@LoginUserAnno LoginUser loginUser, User user) {

return user;

}

@GetMapping("/article/callHello")

public String callHello(String name, @LoginUserAnno LoginUser loginUser) {

}

loginUser会自动进行注入,然后就可以拿到我们想要的数据了,这个其实是属于参数注入这块的,在这边做验证显示不合适,验证还是得在过滤器中做,那么问题就是验证完后,拿到用户ID还得传递到HandlerMethodArgumentResolver中才可以完全注入的效果,我们可以用ThreadLocal传递,或者请求头,或者参数等方式都可以,因为在HandlerMethodArgumentResolver中可以获取到这些信息。

webRequest.getParameter("name");

webRequest.getHeader("xxx");

如果真要传递的话推荐下面的方式:

// Filter中

httpRequest.setAttribute("userId", 100);

// HandlerMethodArgumentResolver中

webRequest.getAttribute("userId", WebRequest.SCOPE_REQUEST);

文章导到这里就全部结束了,讲解了这么多方式,我个人认为最优的还是在Filter中实现。

推荐理由:

  • 验证和参数设置在一起,不用考虑传递问题

加入星球特权

9851d76669ecf35111fdd63c866390df.png

1、从前端到后端玩转Spring Cloud

2、实战分库分表中间件Sharding-JDBC

3、实战分布式任务调度框架Elastic Job

4、配置中心Apollo实战

5、高并发解决方案之缓存

6、更多课程等你来解锁,20+课程

12a13942bed614319475f5e1359f491c.png

表情包
插入表情
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符
相关推荐
<p> <b><span style="font-size:14px;"></span><span style="font-size:14px;background-color:#FFE500;">【Java面试宝典】</span></b><br /> <span style="font-size:14px;">1、68讲视频课,500道大厂Java常见面试题+100个Java面试技巧与答题公式+10万字核心知识解析+授课老师1对1面试指导+无限次回放</span><br /> <span style="font-size:14px;">2、这门课程基于胡书敏老师8年Java面试经验,调研近百家互联网公司及面试官的问题打造而成,从筛选简历和面试官角度,给出能帮助候选人能面试成功的面试技巧。</span><br /> <span style="font-size:14px;">3、通过学习这门课程,你能系统掌握Java核心、数据库、Java框架、分布式组件、Java简历准备、面试实战技巧等面试必考知识点。</span><br /> <span style="font-size:14px;">4、知识点+项目经验案例,每一个都能做为面试的作品展现。</span><br /> <span style="font-size:14px;">5、本课程已经在线下的培训课程中经过实际检验,老师每次培训结束后,都能帮助同学们运用面试技巧,成功找到更好的工作。</span><br /> <br /> <span style="font-size:14px;background-color:#FFE500;"><b>【超人气讲师】</b></span><br /> <span style="font-size:14px;">胡书敏 | 10年大厂工作经验,8年Java面试官经验,5年线下Java职业培训经验,5年架构师经验</span><br /> <br /> <span style="font-size:14px;background-color:#FFE500;"><b>【报名须知】</b></span><br /> <span style="font-size:14px;">上课模式是什么?</span><br /> <span style="font-size:14px;">课程采取录播模式,课程永久有效,可无限次观看</span><br /> <span style="font-size:14px;">课件、课程案例代码完全开放给你,你可以根据所学知识,自行修改、优化</span><br /> <br /> <br /> <span style="font-size:14px;background-color:#FFE500;"><strong>如何开始学习?</strong></span><br /> <span style="font-size:14px;">PC端:报名成功后可以直接进入课程学习</span><br /> <span style="font-size:14px;">移动端:<span style="font-family:Helvetica;font-size:14px;background-color:#FFFFFF;">CSDN 学院APP(注意不是CSDN APP哦)</span></span> </p>
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页