当前位置:首页 > 科技  > 软件

快手二面:有了Cookie和Session 为什么还要JWT ?你说一下JWT的原理?

来源: 责编: 时间:2024-04-23 18:03:58 10观看
导读引言在业务系统开发中,用户身份验证是保障系统服务安全性的基石。无论是社交网络、电商平台还是企业级应用,都需要确保用户的访问权限与行为与其真实身份相符。为了达到这一目的,我们通常采用了一系列技术手段来管理用户

引言

在业务系统开发中,用户身份验证是保障系统服务安全性的基石。无论是社交网络、电商平台还是企业级应用,都需要确保用户的访问权限与行为与其真实身份相符。为了达到这一目的,我们通常采用了一系列技术手段来管理用户会话并验证其身份,其中最常见的是Cookie和Session机制。但是,在近年来随着微服务以及API驱动架构的发展,JSON Web Tokens(JWT)作为一种轻量级的身份验证方案得到了广泛的关注和应用。u4628资讯网——每日最新资讯28at.com

那么,JWT究竟是什么?它的原理是如何运作的?又为何在已有Cookie和Session的基础上,仍需要引入JWT作为身份验证的一种新方法呢?u4628资讯网——每日最新资讯28at.com

Cookie和Session

Cookie是什么?

Cookie是Web开发中一种关键的客户端存储技术,它是由Web服务器在用户访问时生成并发送至用户浏览器的微型数据包,以文本文件的形式储存在用户的设备上。浏览器根据HTTP协议的规定,在之后对同一服务器的所有请求中自动附带这些Cookie信息。u4628资讯网——每日最新资讯28at.com

服务器通过在HTTP响应头中设置Set-Cookie字段,来指示浏览器保存具有特定名称、值和其他配置参数(例如有效期、安全选项、路径限制、域名限制等)的Cookie。这样,每一个Cookie实质上就是一对键值对,帮助服务器识别和区分不同的用户及其在网站上的交互历史。u4628资讯网——每日最新资讯28at.com

Cookie的一个核心应用是在用户身份验证和会话管理方面,它可存储用户的会话ID,使得用户在不同页面间切换或重新打开网站时,服务器能够基于这个ID重建用户的会话状态,实现无缝登录和个性化体验。此外,Cookie还广泛应用于记录用户喜好、统计分析用户行为、广告定向投放等方面,以提升用户体验和服务质量。但是,由于Cookie可能涉及用户隐私,现代浏览器和相关法规越来越重视对Cookie使用的透明度和用户控制权。u4628资讯网——每日最新资讯28at.com

Seeion是什么?

Session作为一种服务器端机制,旨在管理和维护用户在其交互期间的状态信息,这些信息被安全地存储在服务器内存或者持久化存储(如数据库)中,与仅在客户端存储的Cookie形成了鲜明对比。每当用户开始与服务器建立联系时,服务器会初始化一个新的Session对象,并将其状态数据保存在服务器内部资源中。u4628资讯网——每日最新资讯28at.com

一旦Session建立,服务器会产生一个独一无二的Session标识符——Session ID,该ID随后会通过HTTP响应中的Cookie(通常命名规则包括但不限于JSESSIONID)下发至客户端浏览器进行存储。此后,用户的每一次请求都将携带着这个Session ID,服务器通过解析请求中的Session ID,能够在自身的Session存储区域内检索到相对应的Session对象,从而实时获取并更新用户会话的具体状态。u4628资讯网——每日最新资讯28at.com

Session机制广泛应用在诸如认证授权场景中,用来持久化登录用户的凭证和权限等敏感信息,确保用户在浏览网站的不同页面时仍能维持登录状态和个性化体验。此外,Session还能用于暂存用户会话过程中的临时数据,比如购物车的内容、网页表单填写的中间状态等,直至会话自然终止(如超时)、用户主动登出或清理Session时,这些数据才会失效。u4628资讯网——每日最新资讯28at.com

但是虽然Cookie和Session在会话管理中一直使用,但它们存在一些缺点如下:u4628资讯网——每日最新资讯28at.com

  • Cookie数量和大小限制:浏览器对Cookie的数量和大小有限制,可能导致用户数据存储受限。
  • 服务器资源占用:Session需要在服务器端存储大量用户会话信息,随着并发用户数的增长,可能会造成服务器内存资源紧张。
  • 跨域问题:Cookie默认遵循同源策略,不便于跨域共享会话状态。
  • 分布式系统中的Session同步:在分布式环境或集群部署的情况下,Session数据需要在各服务器之间同步,增加了系统的复杂性。

为解决上述会话管理中的缺点,以及对无状态服务、更好的扩展性和安全性要求,JWT这样的新型身份验证技术就这样诞生了。JWT通过将用户信息加密打包成Token,让客户端自行携带认证信息,从而实现了服务器的无状态化。u4628资讯网——每日最新资讯28at.com

JWT是什么?

JSON Web Tokens (JWT) 是一种开放标准(RFC 7519),定义了一种紧凑、自包含的方式来安全地在各方之间传输信息。JWT主要由三个部分组成,即Header(头部)、Payload(载荷)和Signature(签名),这三部分之间通过.分隔。u4628资讯网——每日最新资讯28at.com

JWT的组成JWT的组成u4628资讯网——每日最新资讯28at.com

JWT的内容采用Base64编码,可以直接嵌入到HTTP请求头或者URL查询参数中,因其具有可读性、自包含和防篡改的特点而广泛应用在身份验证和授权场景中。u4628资讯网——每日最新资讯28at.com

Header

描述了所使用的JWT类型(通常为JWT)以及签名算法(如HS256、RS256等)。u4628资讯网——每日最新资讯28at.com

• typ: 表明这是一个JWT(固定为 "JWT")。u4628资讯网——每日最新资讯28at.com

• alg: 指定用于签署JWT的算法,如 "HS256"(HMAC SHA-256)或 "RS256"(RSA SHA-256)等。u4628资讯网——每日最新资讯28at.com

{  "alg": "HS256",  "typ": "JWT"}

Payload

包含了实际要传递的数据,可以是任意的JSON对象,包含一组称为声明(claims)的数据。一般为用户身份信息(如用户ID、角色、权限等)和其他自定义声明,还包含一个exp(Expiration Time)字段来设置JWT的有效期以及其他元数据。声明分为三种类型:u4628资讯网——每日最新资讯28at.com

  1. Registered Claims(注册声明) 这些是预先定义好的标准声明,虽然不是必须的,但在JWT规范中建议使用。它们提供了一套通用的信息,有助于JWT的标准化处理。常见的注册声明包括:

• iss(issuer):签发JWT的实体。u4628资讯网——每日最新资讯28at.com

• sub(subject):JWT所面向的用户或主题。u4628资讯网——每日最新资讯28at.com

• aud(audience):预期接收JWT的受众。u4628资讯网——每日最新资讯28at.com

• exp(expiration time):JWT过期时间,在此时间之后JWT应被视为无效。u4628资讯网——每日最新资讯28at.com

• nbf(not before):JWT生效时间之前,不应被接受处理的时间点。u4628资讯网——每日最新资讯28at.com

• iat(issued at):JWT的创建时间。u4628资讯网——每日最新资讯28at.com

• jti(JWT ID):JWT的唯一标识符,可用于防止重放攻击。u4628资讯网——每日最新资讯28at.com

2. Public Claims(公共声明) 公共声明是预留用于行业共识的标准声明,虽然目前并未正式注册到IANA JSON Web Token Registry,但可以在IANA JSON Web Token Registry 查看已注册的声明集。如果没有官方注册,但多个项目间需要共享相同的声明,可以选择将声明名称以特定前缀(如 urn:<namespace>:<name>)进行命名,避免冲突。u4628资讯网——每日最新资讯28at.com

3. Private Claims(私有声明) 私有声明是用于应用内部约定的自定义声明,我们可以根据自己业务需求自由定义。例如,可以包含用户ID (userId)、用户角色 (role)、邮箱地址 (email) 等任何想要在JWT中传递的信息。u4628资讯网——每日最新资讯28at.com

示例:u4628资讯网——每日最新资讯28at.com

{  "sub": "1234567890",  "name": "John Doe",  "admin": true,  "iat": 1516239022,  "jti": "1pmysetgujcoden",  "exp": 1516242622,  "scope": [    "read",    "write"  ]}

Payload部分默认是不加密的,一定不要将隐私信息存放在 Payload 当中!!!u4628资讯网——每日最新资讯28at.com

Signature

Signature部分用于确保JWT在传输过程中没有被篡改,它是通过对前两部分(Header和Payload编码后的字符串)使用Header中指定的加密算法以及一个共享或私有的密钥进行签名计算得到的。签名确保了只有知道该密钥的实体才能创建有效的JWT,并且任何人都可以验证JWT的完整性和来源的真实性。u4628资讯网——每日最新资讯28at.com

生成签名的计算公式如下:u4628资讯网——每日最新资讯28at.com

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

算出签名以后,把Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,这个字符串就是JWT 。u4628资讯网——每日最新资讯28at.com

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMiwianRpIjoiMXBteXNldGd1amNvZGVuIiwiZXhwIjoxNTE2MjQyNjIyLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXX0.eW91cmUtYXV0aG9yaXphdGlvbi1zaWduYXR1cmU=

对于这段字符串,我们在JWT官网中,使用它的解码器进行解码,就可以得到Header、Payload、Signature这三部分。u4628资讯网——每日最新资讯28at.com

图片图片u4628资讯网——每日最新资讯28at.com

如何基于JWT进行身份验证?

JWT生成

服务器首先构建JWT的Header和Payload,并分别进行Base64编码。使用Header中指定的签名算法(比如HMAC SHA-256或RSA)对编码后的Header和Payload进行签名,生成Signature。将Header、Payload和Signature连接在一起,形成完整的JWT字符串。u4628资讯网——每日最新资讯28at.com

JWT发放

用户通过用户名/密码登录后,服务器验证用户身份无误,便生成JWT并将其返回给客户端。客户端可以将JWT存储在浏览器的LocalStorage、SessionStorage中,或者作为Bearer Token放在Authorization请求头中。u4628资讯网——每日最新资讯28at.com

这里需要注意,将JWT存储在浏览器的localStorage中,相较于存储在Cookie中,可以降低CSRF(跨站请求伪造)的风险。因为CSRF攻击依赖于浏览器自动附带在请求中的Cookie,而localStorage中的数据不会自动包含在普通的HTTP请求头部。u4628资讯网——每日最新资讯28at.com

至于携带JWT的方式,建议将JWT放在HTTP Header的Authorization字段中,采用Bearer Token的形式:u4628资讯网——每日最新资讯28at.com

Authorization: Bearer <JWT>

这种做法允许在无状态的RESTful API中方便地进行身份验证,服务器可以根据请求头中的JWT来验证请求发起者的身份和权限。同时,这种方式也便于实现JWT的刷新与撤销。u4628资讯网——每日最新资讯28at.com

JWT使用

在后续的请求中,客户端将JWT随HTTP请求一起发送给服务器。服务器接收到JWT后,对其进行解码和验证Signature,如果Signature正确,则说明JWT未被篡改,并且仍处于有效期内。根据Payload中的信息,服务器可以确定用户身份以及相关权限,从而做出相应处理。u4628资讯网——每日最新资讯28at.com

JWT过期与刷新

JWT有一定的生命周期,过了指定的exp字段时间后,服务器不再接受该JWT。对于长时间交互的场景,可以设计Refresh Token机制,当JWT即将过期时,客户端使用Refresh Token去服务器申请新的JWT,以延长用户会话的有效期。u4628资讯网——每日最新资讯28at.com

图片图片u4628资讯网——每日最新资讯28at.com

JWT安全性

JWT如何防止被篡改?

JWT通过签名(Signature)来防止被篡改。数字签名的机制确保了JWT的内容一旦被篡改,服务端就能检测出来,从而拒绝非法的请求。u4628资讯网——每日最新资讯28at.com

当客户端带着JWT向服务器发送请求时,服务器会先将接收到的JWT按.分割成Header、Payload和Signature三部分。服务器使用与生成签名时相同的密钥和算法,根据接收到的Header和Payload重新计算签名。如果重新计算出的签名与接收到的Signature一致,那么可以认为JWT在传输过程中未被篡改;如果不一致,则说明JWT已被篡改。u4628资讯网——每日最新资讯28at.com

当然,JWT通过这种方式防止被篡改的一个大前提就是,秘钥(Secret Key)是安全的,如果秘钥泄漏,那就可以更改Header、Payload,然后利用这个秘钥生成一个Signature就可以了,那么就不安全了。所以,我们一定要确保服务端使用的密钥是安全的,并且严格保密,不对外泄露。u4628资讯网——每日最新资讯28at.com

如何加强JWT的安全性?

作为使用者,防止JWT被篡改的主要措施并不直接体现在客户端的操作上,而是依赖于服务端的安全设计和实施。我们可以通过以下方面考虑,加强JWT的安全性:u4628资讯网——每日最新资讯28at.com

1. 服务端安全策略:首先我们必须确保服务端使用的密钥是安全的,并且严格保密,不对外泄露。密钥用于签署和验证JWT,一旦密钥泄露,可能导致JWT被伪造或篡改。并且还要采用安全强度足够高的签名算法,例如HS256、RS256等,这些算法能有效地确保JWT的完整性。u4628资讯网——每日最新资讯28at.com

2. 正确的签名流程:确保所有签发的JWT都经过服务器端签名,并且每次验证JWT时都要使用相同的密钥和算法进行验证。u4628资讯网——每日最新资讯28at.com

3. 设置合理的Token有效期:设置JWT的有效期(exp claim)限制,避免JWT长时间有效,减少恶意攻击者篡改JWT后继续使用的机会。u4628资讯网——每日最新资讯28at.com

4. 严谨的身份验证逻辑:在服务端验证JWT时,除了验证签名之外,还应对Payload中的其他重要声明(如用户ID、权限、过期时间等)进行校验。u4628资讯网——每日最新资讯28at.com

5. 客户端存储方式:尽管JWT被篡改的风险主要集中在网络传输阶段,但在客户端存储JWT时,推荐使用localStorage而非Cookie(尤其是考虑到防止CSRF攻击),同时也可以考虑对敏感信息加密存储。u4628资讯网——每日最新资讯28at.com

JWT相较于Cookie和Session的优缺点

JWT的优点

JWT是无状态的,这样服务器不需要存储会话状态信息,减轻了服务器端的存储负担,在分布式环境下使用JWT可以在集群内的任何节点验证,无需保持会话状态同步,JWT还可以轻易地在不同域名的服务之间传递,适用于微服务架构和跨域应用。因为它包含了所有必要的用户信息,所以在服务间跳转时无需再次验证用户身份,每个请求都包含了认证所需要的所有信息,减少了服务器查询数据库或存储服务以验证用户状态的次数,从而提高了效率。u4628资讯网——每日最新资讯28at.com

JWT的缺点

JWT中的数据默认情况下是Base64编码的,虽然可以加密但并非强制要求,这使得在不加密的情况下,JWT不适合存储敏感信息。相比而言,Session存储在服务器端,安全性更高。如果JWT被盗取,黑客可以在有效期内持续使用,直到token过期或被撤销。另外JWT的体积相对较大,特别是当包含更多信息时。每次HTTP请求都需要携带JWT,可能会增加请求头的大小,尤其是在移动网络环境下,可能会对带宽和流量有较大影响。并且由于JWT自带有效期,当用户登出时,由于JWT无法立即失效,所以只要JWT发放出去,除非在服务器端维护黑名单或使用可撤销的Token机制(如JWT ID Token配合OpenID Connect协议),否则无法立即废弃或更改Token中的权限信息。u4628资讯网——每日最新资讯28at.com

JWT应用场景

• 身份验证:JWT通常用于用户登录认证,服务器在验证用户凭据成功后,生成一个带有用户特定信息和过期时间的JWT,客户端收到后,在后续请求中将其作为凭证发送给服务器,服务器仅需验证JWT的签名即可确认用户身份,无需持久化存储会话信息。u4628资讯网——每日最新资讯28at.com

• 授权:JWT可以携带权限声明,服务器通过解析JWT中的声明,决定用户是否有权访问特定资源或执行某些操作。u4628资讯网——每日最新资讯28at.com

• 单点登录(SSO):由于JWT可以跨域使用,因此在多个子系统间轻松实现SSO功能,用户在一个系统登录后,其JWT可在多个系统间传递,实现无缝登录体验。u4628资讯网——每日最新资讯28at.com

在高并发、分布式、跨域及移动端场景下,JWT常因其实现的简洁性和高效性而得到青睐,而在注重数据安全性、复杂会话管理和易控制的场景下,Session可能会是更好的选择。同时,很多现代应用采用了混合策略,结合两者的优势来优化用户体验和系统安全性。u4628资讯网——每日最新资讯28at.com

如何使用JWT?

在实际开发中,我们可以使用Spring Boot框架结合JWT来实现用户身份验证。我们来实现一个简单的示例。u4628资讯网——每日最新资讯28at.com

我们引入JWT的依赖包:u4628资讯网——每日最新资讯28at.com

<dependency>    <groupId>io.jsonwebtoken</groupId>    <artifactId>jjwt</artifactId>    <version>0.12.4</version></dependency>

创建JWT工具类

创建一个JWT工具类,用于生成和验证JWT令牌。这个类通常会包含生成JWT、解析JWT以及提取其中载荷信息的方法。u4628资讯网——每日最新资讯28at.com

import io.jsonwebtoken.*;import io.jsonwebtoken.security.Keys;import io.jsonwebtoken.security.SecureDigestAlgorithm;import org.springframework.beans.factory.annotation.Value;import org.springframework.security.core.Authentication;import org.springframework.security.core.userdetails.User;import org.springframework.stereotype.Component;import javax.crypto.SecretKey;import java.util.Date;/** * @version 1.0 * @description: <p></p > * @author: 码农Academy * @create: 2024/4/18 14:39 */@Componentpublic class JwtTokenUtil {    @Value("${security.jwt.token.secret-key}")    private String secretKey;    /**过期时间*/    private static final long VALIDITY_IN_MILLISECONDS = 3600000; // 1h    /**     * 生成JWT     * @param authentication 当前登陆用户信息     * @return JWT     */    public String generateToken(Authentication authentication) {        User user = (User) authentication.getPrincipal();        Date now = new Date();        // 设置过期时间        Date expiryDate = new Date(now.getTime() + VALIDITY_IN_MILLISECONDS);        //指定加密算法        SecureDigestAlgorithm<SecretKey, SecretKey> algorithm = Jwts.SIG.HS256;        //密钥实例        SecretKey key = Keys.hmacShaKeyFor(secretKey.getBytes());        return Jwts.builder()                .subject(user.getUsername())                .signWith(key, algorithm) //设置签名使用的签名算法和签名使用的秘钥                .expiration(expiryDate) //设置过期时间                .claim("username", user.getUsername()) //设置自定义负载信息                .compact();    }    /**     * 验证JWT     * @param authToken     * @return     */    public boolean validateToken(String authToken) {        try {            Jws<Claims> claimsJws = parseJWT(authToken);            claimsJws.getPayload();            return true;        } catch (MalformedJwtException | ExpiredJwtException | UnsupportedJwtException | IllegalArgumentException e) {            return false;        }    }    /**     * 解析jwt     * @param authToken 登陆token     * @return     */    public Jws<Claims> parseJWT(String authToken){        //密钥实例        SecretKey key = Keys.hmacShaKeyFor(secretKey.getBytes());        return Jwts.parser()                .verifyWith(key)  //设置签名的密钥                .build()                .parseSignedClaims(authToken); //设置要解析的jwt    }    /**     * 从JWT中获取用户名     * @param token     * @return     */    public String getUsernameFromToken(String token) {        Jws<Claims> claimsJws = parseJWT(token);        return (String) claimsJws.getPayload().get("username", String.class);    }}

实现JWT过滤器

创建一个自定义的JWT过滤器,它负责检查每个请求的Authorization header中的Bearer Token,并对其进行验证。验证成功后,创建一个Authentication对象,并将其置于SecurityContext中。u4628资讯网——每日最新资讯28at.com

import com.springboot.base.jwt.util.JwtTokenUtil;import org.springframework.security.authentication.AbstractAuthenticationToken;import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;import org.springframework.security.core.GrantedAuthority;import org.springframework.security.core.authority.SimpleGrantedAuthority;import org.springframework.security.core.context.SecurityContextHolder;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.core.userdetails.UserDetailsService;import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;import org.springframework.web.filter.OncePerRequestFilter;import javax.servlet.FilterChain;import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.util.List;import java.util.stream.Collectors;/** * @version 1.0 * @description: <p></p > * @author: 码农Academy * @create: 2024/4/18 16:15 */public class JwtAuthenticationFilter extends OncePerRequestFilter {    private final UserDetailsService userDetailsService;    private final JwtTokenUtil jwtTokenUtil;    private static final String BEARER_PREFIX = "Bearer ";     public JwtAuthenticationFilter(UserDetailsService userDetailsService, JwtTokenUtil jwtTokenUtil) {          this.userDetailsService = userDetailsService;          this.jwtTokenUtil = jwtTokenUtil;     }    @Override    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {        String jwtToken = getTokenFromRequest(request);        if (jwtToken != null && jwtTokenUtil.validateToken(jwtToken)) {            String username = jwtTokenUtil.getUsernameFromToken(jwtToken);            UserDetails userDetails = userDetailsService.loadUserByUsername(username);            // 用于权限校验            List<GrantedAuthority> authorities = userDetails.getAuthorities().stream()                    .map(authority -> new SimpleGrantedAuthority(authority.getAuthority()))                    .collect(Collectors.toList());            AbstractAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(                    userDetails, null, authorities            );            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));            SecurityContextHolder.getContext().setAuthentication(authentication);        }else {            // 如果出现异常,可能是无效的Token,此处可记录日志或做其他处理            System.out.println("没有登陆。。。。。。");        }        filterChain.doFilter(request, response);    }    private String getTokenFromRequest(HttpServletRequest request) {        String authHeader = request.getHeader("Authorization");        if (authHeader != null && authHeader.startsWith(BEARER_PREFIX)) {            return authHeader.replace(BEARER_PREFIX, "");        }        return null;    }}

配置Spring Security

配置Spring Security以支持JWT身份验证。这通常涉及编写自定义的JWT过滤器,用于拦截请求并在请求头中提取JWT令牌进行验证。过滤器会在验证通过后将用户信息填充到Spring Security的上下文中。u4628资讯网——每日最新资讯28at.com

import com.springboot.base.jwt.filter.JwtAuthenticationFilter;import com.springboot.base.jwt.util.JwtTokenUtil;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.security.authentication.AuthenticationManager;import org.springframework.security.config.annotation.web.builders.HttpSecurity;import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import org.springframework.security.config.http.SessionCreationPolicy;import org.springframework.security.core.userdetails.UserDetailsService;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import org.springframework.security.crypto.password.PasswordEncoder;import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;/** * @version 1.0 * @description: <p></p > * @author: 码农Academy * @create: 2024/4/18 16:11 */@Configuration@EnableWebSecuritypublic class WebSecurityConfig extends WebSecurityConfigurerAdapter {    @Autowired    private JwtTokenUtil jwtTokenUtil;    @Autowired    private UserDetailsService userDetailsService;    @Override    public void configure(HttpSecurity http) throws Exception {        http.csrf().disable()                .authorizeRequests((requests) -> requests                        .antMatchers("/user/login").permitAll() // 登录接口公开                        .anyRequest().authenticated() // 其他接口需要验证                )                .addFilterBefore(new JwtAuthenticationFilter(userDetailsService, jwtTokenUtil), UsernamePasswordAuthenticationFilter.class) // 添加JWT过滤器                .sessionManagement((session) -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); // 关闭Session管理    }    @Override    @Bean    public AuthenticationManager authenticationManagerBean() throws Exception {        return super.authenticationManagerBean();    }    @Bean    public PasswordEncoder passwordEncoder() {        return new BCryptPasswordEncoder();    }}

在实现Spring Security的UserDetailsService的接口,用于登陆时查询用户信息,构造Spring Security的User信息。u4628资讯网——每日最新资讯28at.com

import com.springboot.base.jwt.login.RoleEntity;import com.springboot.base.jwt.login.UserEntity;import com.springboot.base.jwt.repository.UserRepository;import org.springframework.security.core.GrantedAuthority;import org.springframework.security.core.authority.SimpleGrantedAuthority;import org.springframework.security.core.userdetails.User;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.core.userdetails.UserDetailsService;import org.springframework.security.core.userdetails.UsernameNotFoundException;import org.springframework.stereotype.Service;import java.util.Collection;import java.util.List;import java.util.stream.Collectors;/** * @version 1.0 * @description: <p></p > * @author: 码农Academy * @create: 2024/4/18 16:22 */@Servicepublic class UserDetailsServiceImpl implements UserDetailsService {    private final UserRepository userRepository;    public UserDetailsServiceImpl(UserRepository userRepository) {        this.userRepository = userRepository;    }    @Override    public UserDetails loadUserByUsername(String username) {        // 根据用户名查询用户实体        UserEntity userEntity = userRepository.findByUserName(username);        // 如果用户不存在,则抛出异常        if (userEntity == null) {            throw new UsernameNotFoundException("User not found with username: " + username);        }        // 将用户实体转换为Spring Security可识别的UserDetails对象        User user = new User(userEntity.getUserName(),                userEntity.getPassword(), // 注意:此处假设password字段是已经经过加密处理过的                userEntity.getEnabled(),                true, true, true,                getAuthorities(userEntity.getRoleList()) // 获取用户的角色并转换为GrantedAuthority列表        );        System.out.println(user.getPassword());        return user;    }    private Collection<? extends GrantedAuthority> getAuthorities(List<RoleEntity> roles) {        return roles.stream()                .map(role -> new SimpleGrantedAuthority(role.getRoleCode()))                .collect(Collectors.toList());    }}

上述使用Spring Security,如果有疑问的东西可以去查一下Spring Security的工作原理。u4628资讯网——每日最新资讯28at.com

登陆接口

我们在定义一个登陆接口,登陆成功后返回jwt生成的token。u4628资讯网——每日最新资讯28at.com

import com.springboot.base.jwt.request.LoginRequest;import com.springboot.base.jwt.response.JwtResponse;import com.springboot.base.jwt.util.JwtTokenUtil;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.http.HttpStatus;import org.springframework.http.ResponseEntity;import org.springframework.security.authentication.AuthenticationManager;import org.springframework.security.authentication.BadCredentialsException;import org.springframework.security.authentication.DisabledException;import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;import org.springframework.security.core.Authentication;import org.springframework.security.core.AuthenticationException;import org.springframework.security.core.context.SecurityContextHolder;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;/** * @version 1.0 * @description: <p></p > * @author: 码农Academy * @create: 2024/4/18 17:31 */@RestController@RequestMapping("/user")public class LoginController {    @Autowired    private JwtTokenUtil jwtTokenUtil;    @Autowired    private AuthenticationManager authenticationManager;    @PostMapping("/login")    public ResponseEntity<?> createAuthenticationToken(@RequestBody LoginRequest loginRequest) throws Exception {        // 创建基于用户名密码的Authentication请求对象        UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(                loginRequest.getUserName(),                loginRequest.getPassword()        );        try {            // 使用AuthenticationManager进行身份验证            Authentication authentication = authenticationManager.authenticate(authToken);            // 如果身份验证成功,SecurityContextHolder会存储该Authentication对象            SecurityContextHolder.getContext().setAuthentication(authentication);            // 生成JWT Token或其他形式的授权凭证            String token = jwtTokenUtil.generateToken(authentication);            // 返回带有Token的响应            return ResponseEntity.ok(new JwtResponse(token));        } catch (BadCredentialsException ex) {            // 处理用户名/密码不正确等错误情况            System.out.println("密码不正确");            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Invalid credentials");        } catch (DisabledException ex) {            // 处理账户被禁用等情况            System.out.println("处理账户被禁用");            return ResponseEntity.status(HttpStatus.FORBIDDEN).body("Account is disabled");        } catch (AuthenticationException e) {            System.out.println("Authentication failed");            // 其他身份验证异常            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Authentication failed");        }    }}
import com.fasterxml.jackson.annotation.JsonCreator;import lombok.Data;/** * @version 1.0 * @description: <p></p > * @author: 码农Academy * @create: 2024/4/18 18:59 */@Datapublic class JwtResponse {    private String token;    private String type = "Bearer";    public JwtResponse(String token) {        this.token = token;    }    @JsonCreator    public static JwtResponse of(String token) {        return new JwtResponse(token);    }}

我们模拟一下登陆发起请求:u4628资讯网——每日最新资讯28at.com

登陆成功,返回token。u4628资讯网——每日最新资讯28at.com

给一个错误密码,登陆失败:u4628资讯网——每日最新资讯28at.com

我们将token设置到Authorization中,发起其他业务请求:u4628资讯网——每日最新资讯28at.com

接口请求成功。u4628资讯网——每日最新资讯28at.com

我们改变一下token值,在发起请求:u4628资讯网——每日最新资讯28at.com

后台打印token校验失败,即未登录。u4628资讯网——每日最新资讯28at.com

结论

JWT(JSON Web Tokens)作为一种轻量级且灵活的身份验证和授权机制,在现代web服务及移动应用中得到了广泛应用。它允许服务器端通过加密签名的方式向客户端发放安全的、自包含的令牌,这些令牌可以携带必要的用户身份信息和权限声明,而且由于其无需持久化存储的特性,非常适合于微服务架构下的无状态通信场景。u4628资讯网——每日最新资讯28at.com

总结起来,采用JWT进行身份验证具有以下优点:u4628资讯网——每日最新资讯28at.com

• 安全性: 通过密钥签名保证令牌的安全性,防止篡改。u4628资讯网——每日最新资讯28at.com

• 高效性: 状态less设计减少了服务器端存储负担,提升了系统的可扩展性和响应速度。u4628资讯网——每日最新资讯28at.com

• 跨域友好: JWT可轻松应用于多个域名或子系统间的认证需求。u4628资讯网——每日最新资讯28at.com

• 自包含性: 令牌自身携带了足够的用户信息,减轻了服务器端查询数据库的频率。u4628资讯网——每日最新资讯28at.com

• 有效期可控: 可以在JWT中设置过期时间,从而控制用户的登录会话持续时间。u4628资讯网——每日最新资讯28at.com

不过需要注意的是,JWT并非适用于所有场景,尤其是在敏感数据的处理上,因为一旦令牌被截获,黑客们在有效期内可以持续使用。因此,在实施JWT方案时,应充分考虑其适用范围,并配合合适的刷新策略、黑名单机制以及其他安全措施,确保系统的整体安全性和用户体验。u4628资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-84909-0.html快手二面:有了Cookie和Session 为什么还要JWT ?你说一下JWT的原理?

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 面试官:Spring Boot 中的监视器是什么?不是监听器嘛

下一篇: 开放式耳机市场再迎力作 Shokz 韶音春季新品重磅发布

标签:
  • 热门焦点
  • 一加Ace2 Pro官宣:普及16G内存 引领24G

    一加Ace2 Pro官宣:普及16G内存 引领24G

    一加官方今天继续为本月发布的新机一加Ace2 Pro带来预热,公布了内存方面的信息。“淘汰 8GB ,12GB 起步,16GB 普及,24GB 引领,还有呢?#一加Ace2Pro#,2023 年 8 月,敬请期待。”同时
  • Java NIO内存映射文件:提高文件读写效率的优秀实践!

    Java NIO内存映射文件:提高文件读写效率的优秀实践!

    Java的NIO库提供了内存映射文件的支持,它可以将文件映射到内存中,从而可以更快地读取和写入文件数据。本文将对Java内存映射文件进行详细的介绍和演示。内存映射文件概述内存
  • 之家push系统迭代之路

    之家push系统迭代之路

    前言在这个信息爆炸的互联网时代,能够及时准确获取信息是当今社会要解决的关键问题之一。随着之家用户体量和内容规模的不断增大,传统的靠"主动拉"获取信息的方式已不能满足用
  • 如何通过Python线程池实现异步编程?

    如何通过Python线程池实现异步编程?

    线程池的概念和基本原理线程池是一种并发处理机制,它可以在程序启动时创建一组线程,并将它们置于等待任务的状态。当任务到达时,线程池中的某个线程会被唤醒并执行任务,执行完任
  • 多线程开发带来的问题与解决方法

    多线程开发带来的问题与解决方法

    使用多线程主要会带来以下几个问题:(一)线程安全问题  线程安全问题指的是在某一线程从开始访问到结束访问某一数据期间,该数据被其他的线程所修改,那么对于当前线程而言,该线程
  • 从零到英雄:高并发与性能优化的神奇之旅

    从零到英雄:高并发与性能优化的神奇之旅

    作者 | 波哥审校 | 重楼作为公司的架构师或者程序员,你是否曾经为公司的系统在面对高并发和性能瓶颈时感到手足无措或者焦头烂额呢?笔者在出道那会为此是吃尽了苦头的,不过也得
  • 使用AIGC工具提升安全工作效率

    使用AIGC工具提升安全工作效率

    在日常工作中,安全人员可能会涉及各种各样的安全任务,包括但不限于:开发某些安全工具的插件,满足自己特定的安全需求;自定义github搜索工具,快速查找所需的安全资料、漏洞poc、exp
  • iQOO 11S新品发布会

    iQOO 11S新品发布会

    iQOO将在7月4日19:00举行新品发布会,推出杭州亚运会电竞赛事官方用机iQOO 11S。
  • 电博会上海尔智家模拟500平大平层,还原生活空间沉浸式体验

    电博会上海尔智家模拟500平大平层,还原生活空间沉浸式体验

    电博会为了更好地让参展观众真正感受到智能家居的绝妙之处,海尔智家的程传岭先生同样介绍了展会上海尔智家的模拟500平大平层,还原生活空间沉浸式体验。程传
Top