博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
JWT 在 Spring 上的实践
阅读量:7082 次
发布时间:2019-06-28

本文共 4356 字,大约阅读时间需要 14 分钟。

  hot3.png

简介

手头的新项目采用 jwt 做客户端验证,而不再使用 cookie,确实方便很多,起码跨域这事不用考虑了。

jwt 是什么之类的就不多说了,这玩意的介绍满大街都是,这儿只是简单介绍下我在使用过程中的一些处理方式。

目的

这个 API 接口项目中使用 jwt 达成如下效果:

  1. 每个用户的签名都不一样,而不是共用签名,这样即使某人的 jwt 信息泄露,也不会影响其他人
  2. 服务器有专门的表存储用户签名,这样也可以在服务端控制某用户 jwt 的无效化
  3. 定义一个 spring 的 annotation,在 controller 方法的参数里面使用,用于得到用户的 jwt 存储的信息。

实现

采用的 jwt 处理库是 io.jsonwebtoken:jjwt:0.8.0,下面用伪码的方式介绍上述要求的实现过程。

签名方式

jjwt 组件支持自定义签名实现,只需要继承 SigningKeyResolverAdapter 即可:

public class SigningKeyResolverImpl extends SigningKeyResolverAdapter {    private byte[] decode(String secret) {        return TextCodec.BASE64URL.decode(secret);    }    /**     * 从数据库中返回相应的 hashId 用于加密或解密。     *     */    public Optional
getHashId(UUID clientId) { // 数据库读取过程略 return Optional.empty(); } /** * 根据不同的 clientId 对应的 {@link JwtHash} 的 id 生成不同的加密密钥。 * * @param clientId 用户 id * @return */ public byte[] resolveSigningKeyBytes(UUID clientId) { Optional
hashIdOptional = getHashId(clientId); if (hashIdOptional.isPresent()) { String hashId = hashIdOptional.get(); return decode(hashId); } else { throw new IllegalArgumentException("不支持的参数格式"); } } /** * 根据 claims 中 clientId 读取对应的 {@link JwtHash} 表中的 id 作为密钥来解密。 * * @param header * @param claims * @return */ @Override public byte[] resolveSigningKeyBytes(JwsHeader header, Claims claims) { String id = claims.getSubject(); UUID clientId = UUID.fromString(id); Optional
hashIdOptional = getHashId(clientId); if (hashIdOptional.isPresent()) { String hashId = hashIdOptional.get(); return decode(hashId); } return super.resolveSigningKeyBytes(header, claims); }}

然后在生成和解密 jwt 的方法中调用即可:

加密:

Jwts.builder().signWith(SignatureAlgorithm.HS512,                new SigningKeyResolverImpl                    .resolveSigningKeyBytes(clientId))

解密:

Jwts.parser()                .setSigningKeyResolver(new SigningKeyResolverImpl)

定义 spring 的 annotation

其实在 spring 中获得请求头的 Authorization 信息的方法有多种,常用的有拦截器和自定义 annotation,我个人采用的是后者,因为更加清晰,达到的效果为:

@GetMapping("/auth")public RestResponse authDemo(@JwtAuthHeader JwtAuth jwtAuth) {    return new RestResponse("auth success");}

只要是方法中存在 @JwtAuthHeader 定义的参数,就解析 Authorization 头信息,用这种方式还有个好处就是直接对方法做了用户验证了,所以连 spring-security 都省了。

当然,有些时候某些方法虽然需要验证,但是方法体里面其实没有用到 JwtAuth 信息,这个也无所谓,定义此参数,不用就是了。

annotation 定义

@Target(ElementType.PARAMETER)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface JwtAuthHeader {}

HandlerMethodArgumentResolver 实现

public class JwtAuthHeaderHandlerMethodArgumentResolver implements                                                        HandlerMethodArgumentResolver {    @Override    public boolean supportsParameter(MethodParameter parameter) {        return parameter.hasParameterAnnotation(JwtAuthHeader.class);    }    @Override    public Object resolveArgument(MethodParameter parameter,        ModelAndViewContainer mavContainer, NativeWebRequest webRequest,        WebDataBinderFactory binderFactory) throws Exception {        if (parameter.isOptional()) {            throw new IllegalArgumentException("@JwtAuthHeader 参数不支持 Optional");        }        if (!parameter.getParameterType().isAssignableFrom(JwtAuth.class)) {            throw new IllegalArgumentException("@JwtAuthHeader 参数必须是 JwtAuth");        }        String authorization = webRequest.getHeader('Authorization');        // Authorization 头不存在        if (StringUtils.isBlank(authorization)) {            throw new JwtAuthHeaderUnauthorizedException();        }        Optional
jwtAuthOptional = JwtAuthUtil .getJwtAuth(authorization); // jwt 信息解析不匹配,表示没有权限 if (!jwtAuthOptional.isPresent()) { throw new JwtAuthHeaderUnauthorizedException(); } JwtAuth jwtAuth = jwtAuthOptional.get(); return jwtAuth; }}

上述代码抛出的异常,在 @ExceptionHandler 中捕获就可以了。

为使上述代码生效,如果是用的 spring java config,则增加如下代码:

@Configurationpublic class WebMvcConfig extends WebMvcConfigurerAdapter {    @Override    public void addArgumentResolvers(        List
argumentResolvers) { argumentResolvers.add(new JwtAuthHeaderHandlerMethodArgumentResolver()); }}

如果是 xml 配置,也类似,就不提了。

转载于:https://my.oschina.net/boonya/blog/1602892

你可能感兴趣的文章
Java基础学习总结(22)——异常处理
查看>>
Java项目命名规范
查看>>
Maven学习总结(二)——Maven项目构建过程练习
查看>>
nginx相同端口多站点配置
查看>>
运维人员监控web服务器常用命令
查看>>
注销Apache
查看>>
修改redo_logfile组成员和大小
查看>>
Maven学习总结(九)——使用Nexus搭建Maven私服
查看>>
Uva 1606
查看>>
普加项目管理甘特图使用--安装部署
查看>>
RabbitMQ学习总结(3)——入门实例教程详解
查看>>
chmod 命令详解
查看>>
鸟哥的linux私房菜-首次登陆与在线求助1
查看>>
layui模板引擎和Tab标签一起使用
查看>>
FastDFS集群(一张图)
查看>>
shell编程(三)--- 条件判断之中括号和整数测试
查看>>
唯一索引和非唯一索引的区别简析
查看>>
JavaScript中几个特殊的对象:window对象、this对象、global对象
查看>>
Linux下的日志功能
查看>>
ssh自动连接执行命令
查看>>