前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SpringBoot整合JWT实现验证登陆

SpringBoot整合JWT实现验证登陆

作者头像
程序员小明
发布2019-11-08 22:52:42
1.7K0
发布2019-11-08 22:52:42
举报
文章被收录于专栏:程序员小明程序员小明

什么是JWT?

Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计为紧凑且安全的, 特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息, 以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。

JWT是由三段信息构成的,将这三段信息文本用.链接一起就构成了Jwt字符串。就像这样:

代码语言:javascript
复制
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiLnlKjmiLciLCJwYXNzd29yZCI6IjEyMzQ1NiIsImlzcyI6IuetvuWPkeiAhSIsImlkIjoic2xtMTIzIiwiZXhwIjoxNTU1MDQ5NzI1LCJ1c2VybmFtZSI6InNsbSJ9.x5ZoTW5NS4wBCIK61v4YCGi8bsveifBwnsMNBQz8s_s

JWT是由什么构成的?

JWT共有三部分组成,第一部分称之为头部(header),第二部分称之为荷载(payload),第三部分是签名(signature)。

header:

代码语言:javascript
复制
{
  'typ': 'JWT',//声明类型,这里是jwt
  'alg': 'HS256'
}
  • typ:声明了类型
  • alg:声明了加密方式

header经过Base64加密后:

代码语言:javascript
复制
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

Payload:

Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段,供选用

代码语言:javascript
复制
iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号

除了官方字段,你还可以在这个部分定义私有字段,下面就是一个例子。

代码语言:javascript
复制
{
  "sub": "123456",
  "name": "admin",
  "admin": true
}

Payload经过Base64加密后:

代码语言:javascript
复制
eyJzdWIiOiLnlKjmiLciLCJwYXNzd29yZCI6IjEyMzQ1NiIsImlzcyI6IuetvuWPkeiAhSIsImlkIjoic2xtMTIzIiwiZXhwIjoxNTU1MDQ5NzI1LCJ1c2VybmFtZSI6InNsbSJ9

JWT 默认是不加密的,任何人都可以读到,所以不要把秘密信息放在这个部分。

Signature是对前两部分进行的签名防止数据被篡改

首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。

代码语言:javascript
复制
HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。

怎么使用JWT?

一般是在请求头里加入Authorization,并加上Bearer标注:

代码语言:javascript
复制
headers: {
    'Authorization': 'Bearer ' + token
  }

JWT如何应用在Springboot中?

首先引入pom依赖

代码语言:javascript
复制
  <dependency>
      <groupId>com.auth0</groupId>
      <artifactId>java-jwt</artifactId>
      <version>3.4.0</version>
    </dependency>

实现WebMvcConfigurer接口配置拦截所有URl

代码语言:javascript
复制
/**
 * @Author: slm
 * @CreateTime: 2019-04-11 10:07
 * @Description: 拦截设置
 */
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(authenticationInterceptor())
                .addPathPatterns("/**");
    }
    @Bean
    public AuthenticationInterceptor authenticationInterceptor() {
        return new AuthenticationInterceptor();
    }
}

我在项目中使用了全局异常处理和加入了两个自定义注解

  • LoginToken:代表需要对接口进行验证
  • PassToken:代表不需要进行

创建AuthenticationInterceptor实现HandlerInterceptor接口对请求进行拦截

代码语言:javascript
复制
public class AuthenticationInterceptor implements HandlerInterceptor {
    @Autowired
    private UserService userService;

    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
        String token = httpServletRequest.getHeader("token");// 从 http 请求头中取出 token
        // 如果不是映射到方法直接通过
        if (!(object instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) object;
        Method method = handlerMethod.getMethod();
        //检查是否有passtoken注释,有则跳过认证
        if (method.isAnnotationPresent(PassToken.class)) {
            PassToken passToken = method.getAnnotation(PassToken.class);
            if (passToken.required()) {
                return true;
            }
        }
        //检查有没有需要用户权限的注解
        if (method.isAnnotationPresent(LoginToken.class)) {
            LoginToken userLoginToken = method.getAnnotation(LoginToken.class);
            if (userLoginToken.required()) {
                // 执行认证
                if (token == null) {
                    throw new AppException("9999","无token,请重新登录");
                }
                // 获取 token 中的 user id
                Boolean userId;
                try {
                    userId = JWTUtil.verifyToken(token,"123456");
                } catch (JWTDecodeException j) {
                    throw new AppException("401","无权操作");
                }
                if(userId){
                    return true;
                }else {
                    throw new AppException("401","无权操作");
                }
            }
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest,
                           HttpServletResponse httpServletResponse,
                           Object o, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest,
                                HttpServletResponse httpServletResponse,
                                Object o, Exception e) throws Exception {
    }
}

配置完成以后简单模拟一下用户数据:

创建JWTUtil类对token进行生成和校验

代码语言:javascript
复制
@Slf4j
public class JWTUtil {
    private static final String EXP = "exp";

    private static final String PAYLOAD = "payload";


    /**
     * 加密生成token
     *
     * @param object 载体信息
     * @param maxAge 有效时长
     * @param secret 服务器私钥
     * @param <T>
     * @return
     */
    public static String createToken(User object, long maxAge, String secret) {
        try {
            final Algorithm signer = Algorithm.HMAC256(secret);//生成签名
            String token = JWT.create()
                    .withIssuer("签发者")
                    .withSubject("用户")//主题,科目
                    .withClaim("username", object.getUsername())
                    .withClaim("id", object.getId())
                    .withClaim("password",object.getPassword())
                    .withExpiresAt(new Date(System.currentTimeMillis() + maxAge))
                    .sign(signer);
            System.out.println(token);
            return Base64.getEncoder().encodeToString(token.getBytes("utf-8"));
        } catch (Exception e) {
            log.error("生成token异常:", e);
            return null;
        }
    }

    /**
     * 解析验证token
     *
     * @param token  加密后的token字符串
     * @param secret 服务器私钥
     * @return
     */
    public static Boolean verifyToken(String token, String secret) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(secret);
            JWTVerifier verifier = JWT.require(algorithm).build();
            DecodedJWT jwt = verifier.verify(new String(Base64.getDecoder().decode(token),"utf-8"));
            return true;
        } catch (IllegalArgumentException e) {
            throw new AppException("9999",e.getMessage());
        } catch (JWTVerificationException e) {
            throw new AppException("9999",e.getMessage());
        } catch (UnsupportedEncodingException e) {
            throw new AppException("9999",e.getMessage());
        }
    }
}

创建接口调用接口

注意:登录接口加入了PassToken表示不进行校验

最后启动项目,调用登录接口

调用getMsg进行验证

登录成功

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

本文分享自 程序员小明 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是JWT?
    • JWT是由什么构成的?
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档