SpringBoot 整合 JWT 实现 Token 登录验证的简单实现

SpringBoot 整合 JWT 实现 Token 登录验证的实现在实现案例之前,要先去理解 JWT 的概念 以及 它与传统方式的区别和优点。1、什么是 JWT?JSON WEB TOKEN (JWT),是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准(RFC 7519),该 TOKEN 被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其他
摘要由CSDN通过智能技术生成

在实现案例之前,要先去理解 JWT 的概念 以及 它与传统方式的区别和优点。

1、什么是 JWT?

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

2、为什么要使用 JWT?

这里我们说一下基于 TOKEN 的认证传统的 session 认证 的区别。

2.1、传统的 SESSION 认证

什么是无状态协议

无状态 指的是客户端(WEB浏览器)和服务器之间不需要建立持久的连接。当一个客户端向服务器发起请求的时候,服务器收到了请求并且返回响应结果,这次的通信就结束了,同时服务器不保留连接的相关信息。所以每次请求都需要包含所需的所有信息,这样消息结构会比较复杂,同时也会导致相同的数据在多个请求里面反复传输,协议效率也会因此降低。

我们知道,HTTP 协议本身是一种无状态的协议,而这就意味着如果用户向我们的应用提供了用户名和密码来进行用户认证,那么下一次请求的时候,用户还要再一次进行用户认证才行,因为根据 HTTP 协议,我们并不能知道是哪个用户发出的请求,所以为了让我们的应用能识别是哪个用户发出的请求,我们只能在服务器存储一份用户登录的信息,这份登录信息会在响应的时候传递给浏览器,告诉其保存为 COOKIE,以便下次请求的时候发送给我们的应用,这样我们的应用就能识别请求来自哪个用户了,这就是传统的基于 SESSION 认证。

基于SESSION 认证所显露的问题

每个用户经过我们的应用认证之后,我们的用户都要在服务端做一次记录,以便用户下次请求的鉴别,通常而言 SESSION 都是保存在内存中,而随着认证用户的增多,服务端的开销会明显增大。

扩展性:用户认证之后,服务端做认证记录,如果认证的记录被保存在内存中的话,这意味着用户下次请求还必须要请求这台服务器上,这样才嗯那个拿到授权的资源,这样在分布式的应用上,相应的限制了负载均衡的能力。这也意味着限制了应用的扩展能力。

2.2、什么是 CSS 攻击,如何避免?

CSS 攻击,即跨站脚本攻击(Cross Site Scripting),它是 web 程序中最常见的漏洞。

原理:

攻击者会往 WEB 页面里面插入恶意的 HTML 代码(JavaScript、CSS、HTML标签等),当某个用户浏览该页面的时候,嵌入其中的 HTML 代码会被执行,从而达到恶意攻击的用户的目的。如盗取用户 Cookie 执行一系列操作,破坏页面结构,重定向到其他网站。

案例:恶意的在某个网站的登录页面加入 HTML 代码(JavaScript、CSS、HTML标签等),当用户输入账号面面登录后,这段恶意的 HTML 代码就会被执行,从而获取用户的账号密码信息。

预防思路

  • WEB 页面中可由用户输入的地方,对输入的数据转义、过滤处理。
  • 后台输出页面的时候,也需要对输出内容进行转移、过滤处理(因为攻击者可能以其他方式把恶意脚本写入数据库)。
  • 前端对 HTML 标签属性、CSS 属性赋值的地方进行校验。

2.3、什么是 CSRF 攻击,如何避免?

CSRF:Cross Site Request Forgery(跨站点请求伪造)。

CSRF 攻击者在用户已经登录目标网站之后,诱使用户访问一个攻击页面,利用目标网站对用户的信任,以用户身份在攻击页面对目标网站发起伪造用户操作的请求,达到攻击目的。

案例:就好比用户A,在自己电脑上通过自己的账号密码登录了B网站(登录用户身份标识被 XXX 网站信任),当 A 在浏览网页的时候,不经意间打开了钓鱼网站 X,这时候 X 就可以借助着 B 网站对用户A的信任标识,以用户A的身份去访问 B 网站,并对其进行攻击!

预防方法

  • 添加并验证 TOKEN
  • 添加自定义 HTTP 请求头
  • 使用 POST 请求
  • 敏感操作添加验证码

2.4、基于 TOKEN 的鉴权机制

基于 TOKEN 的鉴权机制类似于 HTTP 协议也是无状态的,它不需要在服务端去保留用户的认证信息或者会话信息。这就意味着基于 TOKEN 认证机制的应用不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利。

基本流程

  • 用户使用用户名和密码来请求服务器。
  • 服务器进行验证用户的信息
  • 服务器验证用户名、密码通过,然后发送给用户一个 TOKEN
  • 客户端存储 TOKEN,并在每次请求的时候附送上这个 TOKEN 值
  • 服务端验证 TOKEN 值,并返回数据

这个 TOKEN 必须要在每次请求的时候传递给服务器端,它应该保存在请求头里,另外,服务端要支持 CORS(跨站资源共享)策略,一般我们在服务端这么做就可以了 Access-Control-ALLOW-Origin: *就是支持了所有策略。

分析

Access-Control-ALLOW-Origin: *直译过来就是 访问控制允许同源,这是经常出现于 AJAX 跨域引起的。所谓跨域就是,在 a.com 域下,访问 b.com 域下的资源;处于安全考虑,浏览器允许跨域写,而不允许跨域读。写就是发送请求,Send Request;读就是接受响应,Receive Response;

  • 表单默认提交(GET、POST),超链接访问访问域外的资源这都是允许的,因为在点击超链接或者按钮的时候,浏览器地址已经变了,这就是一个普通的请求,不存在跨域。
  • AJAX (借助 XmlHttpRequest)跨域请求,这是被禁止的,因为 AJAX 就是为了接受响应回来的数据,这违背了不允许跨域读的原则
  • JSONP 属于跨域读且形式限制为 GET 方式,它利用了 Script 标签的特性;这是允许的。因为浏览器把跨域读脚本当作例外,类似的 img、iframe 的 src 属性都可以请求域外资源。

跨域请求前总会先发送一个 options 请求,这是为什么

先来了解一下什么是 OPTIONS 请求

跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站点通过浏览器有权限访问哪些资源。另外,规范要求,对那些可能对服务器产生副作用的 HTTP 请求方法(特别是以 GET 以外的 HTTP 请求,或者搭配一些 MIME 类型的POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许后,才发起实际的 HTTP 请求,在预检请求的返回中,服务端也可以通知服务端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。

总结一下

OPTIONS 请求即预检测请求,可用于检测服务器允许的 HTTP 方法。当发起跨域请求的时候,由于安全原因,触发一定条件的时候浏览器就会在正式请求之前自动线发起 OPTIONS 请求,即 CORS 预检测请求,服务器若接受该跨域请求,浏览器才继续发起正式请求。

想要了解更详细的可以参考这篇博客:为什么会发送OPTIONS请求?

3、JWT 的请求流程

IoV8eA.png

总结

  • 用户使用账户和密码发出 POST 请求
  • 服务器使用私钥创建一个 JWT
  • 服务器返回这个 JWT 给浏览器
  • 浏览器将该 JWT 串在请求头中向服务器发送请求
  • 服务器验证该 JWT
  • 返回响应的资源给浏览器

3.1、JWT 的主要应用场景

身份认证在这种场景下,一旦用户完成了登录,在接下来的每个请求中都需要包含 JWT 串,可以用来验证用户身份以及对路由,服务和资源的访问权限进行验证。由于它的开销很小,可以轻松的在不同域名的系统中传递,所有目前在单点登录(SSO)中比较广泛的使用了该技术。信息交换在通信双方之间使用 JWT 对数据进行编码是一种非常安全的方式,由于它的信息是经过签名的,可以确保发送者发送的信息是没有经过伪造的

优点

  • 简洁:(Compact),可以通过 URL ,POST 参数或者在 HTTP Header 发送,因为数据量小,传输速率也很快。
  • 自包含:(Self-Contained) ,负载中包含了所有用户所需要的信息,避免了多次查询数据库
  • 因为 TOKEN 是以 JSON 加密的形式保存在客户端的,所以 JWT 是跨语言的,原则上任何 web 形式都支持。
  • 不需要再服务器端保存会话信息,特别适合于分布式微服务。

3.2、JWT 的结构

JWT 是由三段信息构成的,将这三段信息文本用,连接再一起就构成了 JWT 字符串

就像这样:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

JWT 包含了三部分

  • Header 头部(标题包含了令牌的元数据,并且包含签名或者加密算法的类型)
  • Payload 负载(类似于飞机上承载的物品)
  • Signature 签名 / 签证

1)Header

JWT 的头部承载了两部分信息:TOKEN 类型和采用的加密算法

{
   
    "alg": "SHA256",
    "type": "JWT"
}

声明类型:这里是 JWT

声明加密的算法:通常直接使用 HMAC、SHA256。

加密算法通常是单向数散列算法,常见的有 MD5、SHA256、HAMC

MD5:(Message-digest algorithm 5)(信息摘要算法)缩写,广泛用于加密和解密技术,常用于文件校验。不管文件多大,经过 MD5 后都能生成唯一的 MD5 值。

SHA:(Secure Hash Algorithm。安全散列算法),数字签名等密码学应用中重要的工具,安全性高于 MD5

HMAC:(Hash Message Authentication Code),散列消息鉴别码,基于密钥的 Hash 算法的认证协议。用公开函数和密钥产生一个固定长度的值作为认证标识,用这个标识鉴别消息的完整性。常用于接口签名验证

2)Payload

载荷就是存放有效信息的地方。

有效信息包含三个部分

  • 标准中注册的声明
  • 公共的声明
  • 私有的声明

标准中注册的声明(建议但不强制使用)

iss:JWT 签发者

sub:面对的用户(JWT 所面对的用户)

aud:接收 JWT 的一方

exp:过期的时间戳(JWT 的过期时间,这个过期时间必须要大于签发的时间)

nbf:定义在什么时间之前,该 JWT 都是不可用的

iat:JWT 的签发时间

jti:JWT 的唯一身份标识,主要用来作为一次性TOKEN,从而回避重放攻击

公共的声明

公共的声明可以添加任何的信息,一般添加用户的相关信息或者其他业务需要的必要信息,但是不建议添加敏感信息,因为该部分在客户端可以解密

私有的声明

私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为 base64 是对称解密的,意味着该部分信息可以归类于明文信息。

3)Signature

JWT 的第三部分是一个签证信息

这个部分需要 base64 加密后的 headerbase64 加密后的 payload 使用,连接组成的字符串,然后通过 header 中声明的加密方式进行 secret 组合加密,然后就构成了 JWT 的第三部分。

密钥 secret 是保存在服务端的,服务端会根据这个密钥进行生成 TOKEN 和进行验证,所以需要保护好。

SpringBoot 整个 JWT 实现 TOKEN 登录验证的简单实现

引入 Pom 依赖:

<!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->
<dependency>
  <groupId>com.auth0</groupId>
  <artifactId>java-jwt</art
  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Spring Boot 集成 JWT 验证 Token 的步骤如下: 1. 引入相关依赖包 2. 实现自定义 JWT 验证过滤器 3. 在 Security 配置中配置 JWT 验证过滤器 4. 在需要验证的接口上添加 @PreAuthorize 注解 5. 处理 Token 相关操作(生成、刷新、验证等) 请注意,以上步骤仅是大致流程,具体实现可能会有所不同。如需细节请参考Spring Boot 官方文档或搜索相关教程。 ### 回答2: Spring Boot是一款非常方便的框架,可以快速搭建应用程序。同样的,JWT(JSON Web Token)也是一种非常流行的身份验证机制,使用标准的JSON格式,并且使用了数字签名方式验证身份。本文将介绍Spring Boot整合JWT验证Token。 首先,通过pom.xml文件引入相关依赖包: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> ``` 接下来创建JWTUtils类来处理Token的创建与验证: ``` @Component public class JWTUtils { // 密钥 private static final String SECRET = "mySecret"; // 过期时间毫秒 private static final long EXPIRATION_TIME = 1800000; // 创建token public static String createToken(String username, String role) { return Jwts.builder() .setSubject(username) .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME)) .claim("roles", role) .signWith(SignatureAlgorithm.HS512, SECRET) .compact(); } // 验证token public static boolean validateToken(String token) { try { Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token); return true; } catch (SignatureException | ExpiredJwtException e) { return false; } } // 获取用户名 public static String getUsernameFromToken(String token) { return Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody().getSubject(); } // 获取角色 public static String getRoleFromToken(String token) { return Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody().get("roles").toString(); } } ``` 其中,createToken方法用于创建Token,validateToken方法用于验证Token,getUsernameFromToken和getRoleFromToken用于获取Token中的信息。 接下来,在Spring Boot中配置WebSecurityConfig: ``` @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() .authorizeRequests() .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() // 跨域请求会先进行一次options请求 .antMatchers("/auth/**").permitAll() // 登录注册接口放行 .anyRequest().authenticated() .and() .addFilter(new JWTAuthenticationFilter(authenticationManager())) .addFilter(new JWTAuthorizationFilter(authenticationManager())) .headers().cacheControl(); } @Override public void configure(WebSecurity web) { web.ignoring().antMatchers( "/error", "/v2/api-docs", "/configuration/ui", "/swagger-resources/**", "/configuration/**", "/swagger-ui.html", "/webjars/**"); } @Autowired public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { authenticationManagerBuilder.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder()); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } } ``` 其中,configure方法配置了安全规则,addFilter方法添加了JWT的过滤器,configureAuthentication方法配置用户认证方式。 最后,在Controller中使用接口进行调用: ``` @RestController public class UserController { @Autowired private UserService userService; @PostMapping("/auth/register") public String register(@RequestBody UserDTO userDTO) { if (userService.register(userDTO)) { return "注册成功"; } else { return "注册失败"; } } @PostMapping("/auth/login") public ResponseEntity<TokenDTO> login(@RequestParam String username, @RequestParam String password) { String role = "ROLE_USER"; // 这里演示只有ROLE_USER角色 String token = JWTUtils.createToken(username, role); return ResponseEntity.ok(new TokenDTO(token)); } @GetMapping("/user/info") public String userInfo(Authentication authentication) { String username = authentication.getName(); String role = JWTUtils.getRoleFromToken(((JwtUserDetails) authentication.getPrincipal()).getToken()); return username + "-" + role; } } ``` 其中,login方法用于登录获取Token,userInfo方法用于获取当前用户信息。这里使用了Spring Security提供的authentication对象来获取用户信息。 综上,Spring Boot整合JWT验证Token并不复杂,通过相关依赖包、JWTUtils、WebSecurityConfig和Controller的配置,即可完成Token身份验证,保证接口的安全性。 ### 回答3: Spring Boot是一个Java开发框架,它可以减少Web应用程序的开发时间和复杂性。在开发Web应用程序时,安全性是一个关键问题,可以使用JWT(Json Web Token)来提高应用程序的安全性。 JWT是一种基于JSON的安全令牌,可以在用户与应用程序之间传递信息。JWT包含了许多有用的信息,例如用户的ID、角色、权限等等。在Spring Boot中,我们可以使用jwt.io上的库来生成和解码JWT令牌。 首先,在pom.xml文件中添加jwt库的依赖项,如下所示: ``` <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-api</artifactId> <version>0.11.2</version> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-impl</artifactId> <version>0.11.2</version> <scope>runtime</scope> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt-jackson</artifactId> <version>0.11.2</version> <scope>runtime</scope> </dependency> ``` 接下来,我们需要创建一个JWTUtils工具类。该类将用于编码、解码和验证JWT令牌。实现代码如下: ``` import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import java.util.Date; @Component public class JWTUtils { @Value("${jwt.secret}") private String secret; @Value("${jwt.expiration}") private Long expiration; public String generateToken(String username) { Date now = new Date(); Date expiryDate = new Date(now.getTime() + expiration * 1000); return Jwts.builder() .setSubject(username) .setIssuedAt(now) .setExpiration(expiryDate) .signWith(SignatureAlgorithm.HS512, secret) .compact(); } public String getUsernameFromToken(String token) { Claims claims = Jwts.parser() .setSigningKey(secret) .parseClaimsJws(token) .getBody(); return claims.getSubject(); } public boolean validateToken(String token, UserDetails userDetails) { String username = getUsernameFromToken(token); return (username.equals(userDetails.getUsername()) && !isTokenExpired(token)); } private boolean isTokenExpired(String token) { Date expiryDate = Jwts.parser() .setSigningKey(secret) .parseClaimsJws(token) .getBody() .getExpiration(); return expiryDate.before(new Date()); } } ``` 在上面的代码中,我们使用@Value注释从application.properties文件中读取秘密和过期时间。generateToken方法是用于生成JWT令牌的方法。setSubject()定义了JWT令牌的主题,setIssuedAt()是JWT生成时间,setExpiration()是JWT令牌的超时时间。最后,我们使用signWith()进行签名和压缩。 getUsernameFromToken()方法获取JWT令牌中的用户名。validateToken()用于验证JWT令牌是否有效。isTokenExpired()方法检查JWT令牌是否已过期。 为了让Spring Boot知道我们在哪里可以找到JWTUtils类,我们需要在Application.java类中添加@ComponentScan注解,如下所示: ``` @SpringBootApplication @ComponentScan({"com.example.jwt"}) public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` 最后,我们在Spring Boot中创建一个Controller类。该类有一个login()方法,该方法接收用户名和密码并验证该用户是否存在。如果用户存在,则生成JWT令牌并将其返回给客户端。如下所示: ``` import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.AuthenticationException; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.web.bind.annotation.*; import com.example.jwt.JWTUtils; @RestController @RequestMapping("/api/auth") public class AuthController { @Autowired private AuthenticationManager authenticationManager; @Autowired private JWTUtils jwtUtils; @Autowired private UserDetailsService userDetailsService; @PostMapping("/login") public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) throws AuthenticationException { try { authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword())); } catch (UsernameNotFoundException e) { throw new RuntimeException("User not found"); } final UserDetails userDetails = userDetailsService.loadUserByUsername(loginRequest.getUsername()); final String token = jwtUtils.generateToken(userDetails.getUsername()); return ResponseEntity.ok(new AuthResponse(token)); } } ``` 在上面的代码中,我们使用@Autowired注释从其他类中获取所需的依赖项。login()方法中,我们使用authenticationManager.authenticate()方法验证用户是否存在。如果该用户不存在,则会抛出一个异常。然后,我们使用userDetailsService.loadUserByUsername()方法加载用户详细信息。最后,我们调用JWTUtils.generateToken()方法生成JWT令牌。 在上面的代码中,我们还创建了一个名为AuthResponse的简单类,该类是返回的响应对象,包含JWT令牌。 ``` public class AuthResponse { private String token; public AuthResponse(String token) { this.token = token; } public String getToken() { return token; } public void setToken(String token) { this.token = token; } } ``` 以上就是Spring Boot整合JWT验证token简单代码示例。该代码示例可以让开发者更加容易地理解Spring Boot如何使用JWT来增强应用程序的安全性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值