溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

springboot怎么集成JWT實現身份認證

發布時間:2023-05-08 14:27:09 來源:億速云 閱讀:79 作者:zzz 欄目:開發技術

Spring Boot 怎么集成 JWT 實現身份認證

目錄

  1. 引言
  2. JWT 簡介
  3. Spring Boot 集成 JWT 的準備工作
  4. JWT 的生成與解析
  5. Spring Security 集成 JWT
  6. JWT 在 Spring Boot 中的應用
  7. JWT 的安全性考慮
  8. JWT 的優缺點
  9. 總結
  10. 參考資料

引言

在現代的 Web 應用中,身份認證是一個非常重要的環節。傳統的基于 Session 的認證方式雖然簡單易用,但在分布式系統中存在一些問題,比如 Session 的存儲和管理。為了解決這些問題,JWT(JSON Web Token)應運而生。JWT 是一種輕量級的認證機制,它可以在客戶端和服務器之間安全地傳輸信息,并且不需要在服務器端存儲 Session。

本文將詳細介紹如何在 Spring Boot 中集成 JWT 實現身份認證。我們將從 JWT 的基本概念開始,逐步講解如何在 Spring Boot 項目中生成和解析 JWT,并將其與 Spring Security 集成,最終實現一個完整的身份認證系統。

JWT 簡介

JWT 的結構

JWT 由三部分組成,分別是 Header、Payload 和 Signature。這三部分通過 . 連接在一起,形成一個完整的 JWT。

  • Header:包含 token 的類型(通常是 JWT)和使用的簽名算法(如 HMAC SHA256 或 RSA)。
  • Payload:包含聲明(claims),聲明是關于實體(通常是用戶)和其他數據的聲明。聲明分為三種類型:注冊聲明、公共聲明和私有聲明。
  • Signature:用于驗證消息在傳輸過程中沒有被篡改。簽名是通過將編碼后的 Header 和 Payload 與一個密鑰一起進行簽名生成的。

JWT 的工作原理

JWT 的工作原理可以簡單概括為以下幾個步驟:

  1. 用戶登錄:用戶通過提供用戶名和密碼進行登錄。
  2. 生成 JWT:服務器驗證用戶憑證后,生成一個 JWT 并返回給客戶端。
  3. 客戶端存儲 JWT:客戶端將 JWT 存儲在本地(通常是 localStorage 或 cookie)。
  4. 發送請求:客戶端在每次請求時,將 JWT 放在請求頭中發送給服務器。
  5. 驗證 JWT:服務器收到請求后,驗證 JWT 的有效性,并根據 JWT 中的信息進行授權。

Spring Boot 集成 JWT 的準備工作

創建 Spring Boot 項目

首先,我們需要創建一個 Spring Boot 項目??梢允褂?Spring Initializr 快速生成一個項目骨架。選擇以下依賴:

  • Spring Web
  • Spring Security
  • Spring Data JPA
  • H2 Database(用于測試)

添加依賴

pom.xml 中添加以下依賴:

<dependencies>
    <!-- Spring Boot Starter Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Spring Boot Starter Security -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    <!-- Spring Boot Starter Data JPA -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-jpa</artifactId>
    </dependency>

    <!-- H2 Database -->
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>runtime</scope>
    </dependency>

    <!-- JWT -->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.1</version>
    </dependency>

    <!-- Lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided</scope>
    </dependency>
</dependencies>

JWT 的生成與解析

生成 JWT

在生成 JWT 之前,我們需要定義一個工具類來處理 JWT 的生成和解析。以下是一個簡單的 JWT 工具類:

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

@Component
public class JwtUtil {

    private String SECRET_KEY = "secret";

    public String extractUsername(String token) {
        return extractClaim(token, Claims::getSubject);
    }

    public Date extractExpiration(String token) {
        return extractClaim(token, Claims::getExpiration);
    }

    public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = extractAllClaims(token);
        return claimsResolver.apply(claims);
    }

    private Claims extractAllClaims(String token) {
        return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();
    }

    private Boolean isTokenExpired(String token) {
        return extractExpiration(token).before(new Date());
    }

    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        return createToken(claims, userDetails.getUsername());
    }

    private String createToken(Map<String, Object> claims, String subject) {
        return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10))
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY).compact();
    }

    public Boolean validateToken(String token, UserDetails userDetails) {
        final String username = extractUsername(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }
}

解析 JWT

在上面的工具類中,我們已經實現了 JWT 的解析功能。通過 extractAllClaims 方法,我們可以從 JWT 中提取所有的聲明。extractUsernameextractExpiration 方法分別用于提取用戶名和過期時間。

Spring Security 集成 JWT

配置 Spring Security

接下來,我們需要配置 Spring Security 以支持 JWT 認證。首先,創建一個配置類 SecurityConfig

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService myUserDetailsService;

    @Autowired
    private JwtRequestFilter jwtRequestFilter;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserDetailsService);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        httpSecurity.csrf().disable()
                .authorizeRequests().antMatchers("/authenticate").permitAll()
                .anyRequest().authenticated().and()
                .exceptionHandling().and().sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

自定義 UserDetailsService

我們需要實現一個自定義的 UserDetailsService 來加載用戶信息。以下是一個簡單的實現:

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.ArrayList;

@Service
public class MyUserDetailsService implements UserDetailsService {

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        return new User("foo", "foo", new ArrayList<>());
    }
}

實現 JWT 過濾器

為了實現 JWT 認證,我們需要創建一個過濾器來攔截請求并驗證 JWT。以下是一個簡單的 JWT 過濾器實現:

import io.jsonwebtoken.ExpiredJwtException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
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;

@Component
public class JwtRequestFilter extends OncePerRequestFilter {

    @Autowired
    private MyUserDetailsService userDetailsService;

    @Autowired
    private JwtUtil jwtUtil;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {

        final String authorizationHeader = request.getHeader("Authorization");

        String username = null;
        String jwt = null;

        if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
            jwt = authorizationHeader.substring(7);
            username = jwtUtil.extractUsername(jwt);
        }

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {

            UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);

            if (jwtUtil.validateToken(jwt, userDetails)) {

                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                usernamePasswordAuthenticationToken
                        .setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
            }
        }
        chain.doFilter(request, response);
    }
}

JWT 在 Spring Boot 中的應用

用戶登錄與 JWT 生成

在用戶登錄時,我們需要生成一個 JWT 并返回給客戶端。以下是一個簡單的登錄控制器:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.*;

@RestController
public class AuthenticationController {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private MyUserDetailsService userDetailsService;

    @Autowired
    private JwtUtil jwtUtil;

    @RequestMapping(value = "/authenticate", method = RequestMethod.POST)
    public ResponseEntity<?> createAuthenticationToken(@RequestBody AuthenticationRequest authenticationRequest) throws Exception {

        try {
            authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(authenticationRequest.getUsername(), authenticationRequest.getPassword())
            );
        } catch (BadCredentialsException e) {
            throw new Exception("Incorrect username or password", e);
        }

        final UserDetails userDetails = userDetailsService.loadUserByUsername(authenticationRequest.getUsername());

        final String jwt = jwtUtil.generateToken(userDetails);

        return ResponseEntity.ok(new AuthenticationResponse(jwt));
    }
}

保護 API 端點

在 Spring Security 配置中,我們已經將所有請求設置為需要認證?,F在,我們可以創建一個受保護的 API 端點來測試 JWT 認證:

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @RequestMapping({ "/hello" })
    public String firstPage() {
        return "Hello World";
    }
}

刷新 JWT

在某些情況下,我們可能需要刷新 JWT??梢酝ㄟ^以下方式實現:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.bind.annotation.*;

@RestController
public class RefreshTokenController {

    @Autowired
    private MyUserDetailsService userDetailsService;

    @Autowired
    private JwtUtil jwtUtil;

    @RequestMapping(value = "/refresh", method = RequestMethod.POST)
    public ResponseEntity<?> refreshAuthenticationToken(@RequestHeader("Authorization") String token) throws Exception {

        String username = jwtUtil.extractUsername(token.substring(7));

        UserDetails userDetails = userDetailsService.loadUserByUsername(username);

        if (jwtUtil.validateToken(token.substring(7), userDetails)) {
            String newToken = jwtUtil.generateToken(userDetails);
            return ResponseEntity.ok(new AuthenticationResponse(newToken));
        } else {
            throw new Exception("Invalid token");
        }
    }
}

JWT 的安全性考慮

JWT 的存儲

JWT 通常存儲在客戶端的 localStorage 或 cookie 中。為了增加安全性,建議將 JWT 存儲在 HttpOnly 的 cookie 中,以防止 XSS 攻擊。

JWT 的過期與刷新

JWT 應該設置一個合理的過期時間,以防止長期有效的 token 被濫用??梢酝ㄟ^刷新 token 機制來延長用戶的會話時間。

JWT 的簽名與加密

JWT 的簽名算法應該選擇安全的算法,如 HMAC SHA256 或 RSA。對于敏感信息,建議對 JWT 進行加密。

JWT 的優缺點

優點

  • 無狀態:JWT 是無狀態的,服務器不需要存儲 Session,適合分布式系統。
  • 跨域支持:JWT 可以輕松實現跨域認證。
  • 安全性:JWT 可以通過簽名和加密保證數據的安全性。

缺點

  • 無法撤銷:一旦 JWT 簽發,無法在有效期內撤銷,除非使用黑名單機制。
  • 存儲問題:JWT 存儲在客戶端,可能會被竊取或濫用。
  • 性能問題:JWT 的體積較大,可能會增加網絡傳輸的開銷。

總結

本文詳細介紹了如何在 Spring Boot 中集成 JWT 實現身份認證。我們從 JWT 的基本概念開始,逐步講解了如何在 Spring Boot 項目中生成和解析 JWT,并將其與 Spring Security 集成,最終實現了一個完整的身份認證系統。通過本文的學習,你應該能夠在自己的 Spring Boot 項目中輕松集成 JWT,并實現安全的身份認證機制。

參考資料

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女