JWT与传统Token机制对比:实现安全高效的用户认证

        在现代Web应用程序中,用户认证是一个至关重要的环节。传统的Session机制虽然简单易用,但在分布式系统中却面临诸多挑战。相比之下,JSON Web Tokens(JWT)作为一种轻量级的认证机制,因其无需依赖服务器端存储用户状态的特点而受到广泛青睐。本文将深入探讨JWT的工作原理、优缺点以及在实际项目中的整合策略。

一、JWT简介

        JWT是一种开放标准(RFC 7519),它定义了一种自包含的凭证,用于在各方之间以安全的方式传输信息。JWT由三部分组成:Header(头部)、Payload(载荷)和Signature(签名)。

  • Header:包含了令牌类型和签名算法的信息。例如:

    {
        "alg":"HS256",
        "type","JWT"
    }
    
  • Payload:是JWT的主题部分,包含了一系列声明(Claims)。声明是关于主体的信息,通常用来传输用户数据。例如:

    1{
    2  "userId": "1",
    3  "username": "mayikt"
    4}
  • Signature:用于验证数据的完整性和确认JWT是否被篡改。它是通过将Header、Payload和一个密钥(Secret)按照指定算法计算得出的结果。这个密钥只能由服务端持有,并且用于验证JWT的有效性。

        其中,标准中注册的声明有

  • iss: jwt签发者
  • sub: jwt所面向的用户
  • aud: 接收jwt的一方
  • exp: jwt的过期时间,这个过期时间必须要大于签发时间
  • nbf: 定义在什么时间之前,该jwt都是不可用的.
  • iat: jwt的签发时间
  • jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。

二、JWT vs 传统Token

2.1 Session机制的局限性

        在传统的Web应用中,Session是最常用的用户状态管理方案。然而,随着微服务架构的兴起,Session机制面临着如下挑战:

  • 集群环境下共享困难:在分布式系统中,由于Session数据通常存储在单台服务器上,导致其他节点无法访问到同一用户的Session信息,这限制了系统的横向扩展能力。

2.2 传统Token机制

        传统的Token机制通常指通过用户登录生成一个唯一的标识符(Token),并将此Token与用户ID关联后存储在Redis等缓存数据库中。客户端每次请求时都需要携带这个Token,服务器则通过查询Redis来验证Token的有效性。

        这种方式虽然解决了Session在分布式环境下的共享问题,但仍存在以下不足:

  • 隐藏了数据的真实性:由于Token本身并不包含任何用户信息,因此服务器必须每次都查询数据库来获取用户详情。

2.3 JWT的优势

与上述两种方案相比,JWT具有以下明显优点:

  1. 减轻服务器端压力:JWT将用户信息直接编码在Token内,无需每次请求都查询数据库。
  2. 轻量级且易于解析:采用JSON格式,便于前后端之间的数据交换。
  3. 跨语言支持:由于其基于标准的开放协议,可以在不同编程语言之间无缝集成。

三、JWT在项目中的应用

3.1 Maven依赖配置

        要在Spring Boot项目中使用JWT,直接引入相关依赖即可:

     <!--   jwt 依赖     -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

        之后即可通过生成或解析token,示例如下:

//存入用户信息,生成token
// jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
JwtBuilder jwtBuilder = Jwts.builder()
    .setId("8888")
    .setSubject("Rose")      // jwt所面向的用户
    .setIssuedAt(new Date())  //创建token的日期
    .signWith(SignatureAlgorithm.HS256,"abcde") //使用的算法,加密的盐值,盐值不能太短,否则报错
    .setExpiration(new Date(System.currentTimeMillis()+过期时间毫秒))  //设置token失效时间
            //可以通过claim方法自定义添加属性到token里面,使用key-value格式,
             // 也可以直接传入map,通过addClaims(map)实现
    .claim("name","admin")
    .claim("logo","xxx.jpg");
//使用jwts解析token
Claims body = Jwts.parser().setSigningKey("abcde") //预先设置的盐值
                                    .parseClaimsJws(token)//要解析的token,jws即签名后的jwt
                                    .getBody();    //获取信息
//通过body获取内部信息
String name = body.get("name");

3.2 整合Spring Security

        为了更好地保护API接口,通常还会结合Spring Security框架来实现基于JWT的安全认证。此时,需要添加额外的依赖:

        <!--添加security提供的jwt包,使用security整合oauth2.0-->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-jwt</artifactId>
            <version>1.1.1.RELEASE</version>
        </dependency>
3.2.1 配置Token存储策略

        设置token存储策略,配置JwtTokenStoreConfig类,使用jwt存储token

/**
 * jwt配置类,
 * 1,设置token存储方式为jwt存储,并将TokenStore注入的spring容器
 *
 *,2,配置jwt生成token的返回实现类,JwtAccessTokenConverter,设置秘钥,返回JwtAccessTokenConverter对象,注入容器
 */
@Configuration
public class JwtTokenStoreConfig{

    /**
     *  设置token存储方式为jwt存储
     * @return
     */
    @Bean
    public TokenStore jwtTokenStore(){
        return new JwtTokenStore(jwtAccessTokenConverter());
    }

    //配置jwt生成token的返回实现类
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter(){
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        //配置jwt使用的秘钥
        converter.setSigningKey("test_key");
        return converter;
    }

    //将jwt内容增强器注入为bean
    @Bean
    public JwtTokenEnhancer jwtTokenEnhancer(){
        return new JwtTokenEnhancer();
    }
}

        其中,JwtTokenEnhancer类用于在JWT中添加自定义信息:

/**
 * jwt内容增强器,即通过该类可以在token当中添加自定义的属性
 *
 *
 */
public class JwtTokenEnhancer implements TokenEnhancer {
    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {

        Map<String, Object> info = new HashMap<>();
        info.put("enhance","enhance info");
        //oAuth2AccessToken向上转型为DefaultOAuth2AccessToken,获取方法setAdditionalInformation,通过该方法添加内容到token
        ((DefaultOAuth2AccessToken)oAuth2AccessToken).setAdditionalInformation(info);
        return oAuth2AccessToken;
    }
}
3.2.2 客户端授权配置
    @Autowired
    private PasswordEncoder passwordEncoder;
    /**
     * 预先设置TokenStore(token存储方式)为jwt存储,并注入spring容器,redis存储无需配置token转换器
     * 此处需要配合@Qualifier注解使用name属性注入,因为TokenStore类型的实现类不止一个,type类型的注入无法识别应该注入哪一个bean
     */
    @Autowired
    @Qualifier("jwtTokenStore")
    private TokenStore tokenStore;

    //注入accessToken和JwtToken的相互转换的处理类
    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    //自动注入jwt内容增强器
    @Autowired
    private JwtTokenEnhancer jwtTokenEnhancer;


    /**
     ** 密码模式需要重写该方法,并且需要提前把AuthenticationManager注入到spring容器
     */
     @Autowired
     private UserService userService;

     @Autowired
     private AuthenticationManager authenticationManager;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        //配置jwt内容增强,即额外向token添加自定义属性
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        List<TokenEnhancer> delegates = new ArrayList<>() ;
        delegates.add(jwtTokenEnhancer);
        delegates.add(jwtAccessTokenConverter);
        tokenEnhancerChain.setTokenEnhancers(delegates);


        //使用用户手动授权,即开启密码模式
        endpoints.authenticationManager(authenticationManager)
                .userDetailsService(userService)
                //设置token存储方式,此处为jwt存储token
                .tokenStore(tokenStore)
                //使用jwtAccessTokenConverter生成token
                .accessTokenConverter(jwtAccessTokenConverter)
                //添加自定义token属性
                .tokenEnhancer(tokenEnhancerChain);

    }

四、最后

        JWT作为一种新兴的身份验证机制,以其独特的自包含特性解决了传统Session机制在分布式环境下的局限性。它不仅简化了开发流程,提高了系统性能,同时也为构建更加安全可靠的Web应用提供了有力支持。然而,JWT也有其固有的缺陷,比如一旦生成就无法修改等问题。因此,在实际应用中,开发者需根据具体场景灵活选择最合适的认证方案。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值