SpringBoot整合JWT实现验证登陆

本文介绍了JWT(Json web token),它是基于JSON的开放标准,适用于单点登录场景。阐述了JWT由头部、荷载、签名三部分构成,还说明了使用方法。此外,详细讲解了JWT在Springboot中的应用,包括引入依赖、配置拦截器、创建工具类等步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是JWT?

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

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



 
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiLnlKjmiLciLCJwYXNzd29yZCI6IjEyMzQ1NiIsImlzcyI6IuetvuWPkeiAhSIsImlkIjoic2xtMTIzIiwiZXhwIjoxNTU1MDQ5NzI1LCJ1c2VybmFtZSI6InNsbSJ9.x5ZoTW5NS4wBCIK61v4YCGi8bsveifBwnsMNBQz8s_s


JWT是由什么构成的?

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


header:



 
{	
  'typ': 'JWT',//声明类型,这里是jwt	
  'alg': 'HS256'	
}


  • typ:声明了类型

  • alg:声明了加密方式

header经过Base64加密后:



 
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9


Payload:

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



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


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



 
{	
  "sub": "123456",	
  "name": "admin",	
  "admin": true	
}


Payload经过Base64加密后:



 
eyJzdWIiOiLnlKjmiLciLCJwYXNzd29yZCI6IjEyMzQ1NiIsImlzcyI6IuetvuWPkeiAhSIsImlkIjoic2xtMTIzIiwiZXhwIjoxNTU1MDQ5NzI1LCJ1c2VybmFtZSI6InNsbSJ9


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

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

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



 
HMACSHA256(	
  base64UrlEncode(header) + "." +	
  base64UrlEncode(payload),	
  secret)


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

怎么使用JWT?

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



 
headers: {	
    'Authorization': 'Bearer ' + token	
  }


JWT如何应用在Springboot中?

  首先引入pom依赖



 
  <dependency>	
      <groupId>com.auth0</groupId>	
      <artifactId>java-jwt</artifactId>	
      <version>3.4.0</version>	
    </dependency>


 

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



 
/**	
 * @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();	
    }	
}


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

640?wx_fmt=png

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

  • PassToken:代表不需要进行

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



 
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 {	
    }	
}	


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

640?wx_fmt=png

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



 
@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());	
        }	
    }	
}	


创建接口调用接口

640?wx_fmt=png

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

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

640?wx_fmt=png

调用getMsg进行验证

640?wx_fmt=png

登录成功!

推荐阅读

如何写出无法维护的代码

简历写了会Kafka,面试官90%会让你讲讲acks参数对消息持久化的影响

Spring Boot整合Elasticsearch

Java常用分布式锁技术方案

从简历被拒到收割今日头条offer,我花了一年时间

640?wx_fmt=jpeg

点击在看,和我一起帮助更多开发者!


评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值