Spring Boot的安全配置(三)
JWT
JWT(JSON Web Token)是一種用于在網(wǎng)絡(luò)中傳輸安全信息的開放標(biāo)準(zhǔn)(RFC 7519)。它可以在各個服務(wù)之間安全地傳遞用戶認證信息,因為它使用數(shù)字簽名來驗證信息的真實性和完整性。
JWT有三個部分,每個部分用點(.)分隔:
(資料圖)
Header:通常包含JWT使用的簽名算法和令牌類型。Payload:包含有關(guān)用戶或其他主題的聲明信息。聲明是有關(guān)實體(通常是用戶)和其他數(shù)據(jù)的JSON對象。聲明被編碼為JSON,然后使用Base64 URL編碼。Signature:用于驗證消息是否未被篡改并且來自預(yù)期的發(fā)送者。簽名由使用Header中指定的算法和秘鑰對Header和Payload進行加密產(chǎn)生。在Spring Boot中,您可以使用Spring Security和jjwt庫來實現(xiàn)JWT的認證和授權(quán)。下面是一個使用JWT的示例:
@Configuration@EnableWebSecuritypublic class SecurityConfig extends WebSecurityConfigurerAdapter { @Value("${jwt.secret}") private String jwtSecret; @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests() .antMatchers(HttpMethod.POST, "/api/authenticate").permitAll() .anyRequest().authenticated() .and() .addFilter(new JwtAuthenticationFilter(authenticationManager(), jwtSecret)) .addFilter(new JwtAuthorizationFilter(authenticationManager(), jwtSecret)) .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); } @Override public void configure(AuthenticationManagerBuilder auth) throws Exception { auth.authenticationProvider(new JwtAuthenticationProvider(jwtSecret)); }}
在上面的示例中,SecurityConfig
類繼承了WebSecurityConfigurerAdapter
并使用了@EnableWebSecurity
注解啟用Spring Security。configure()
方法使用HttpSecurity
對象來配置HTTP請求的安全性。.csrf().disable()
禁用了CSRF保護。.authorizeRequests()
表示進行授權(quán)請求。.antMatchers(HttpMethod.POST, "/api/authenticate").permitAll()
表示允許POST請求到/api/authenticate
路徑。.anyRequest().authenticated()
表示要求所有其他請求都需要身份驗證。.addFilter(new JwtAuthenticationFilter(authenticationManager(), jwtSecret))
和.addFilter(new JwtAuthorizationFilter(authenticationManager(), jwtSecret))
分別添加JWT認證和授權(quán)過濾器。.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
指定了會話管理策略。
configure()
方法中還有一個configure(AuthenticationManagerBuilder auth)
方法,它使用JwtAuthenticationProvider
類配置身份驗證。在這里,jwtSecret
被注入到JwtAuthenticationProvider
構(gòu)造函數(shù)中,以便在認證過程中使用。
下面是JwtAuthenticationFilter
和JwtAuthorizationFilter
的實現(xiàn):
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter { private final AuthenticationManager authenticationManager; private final String jwtSecret; public JwtAuthenticationFilter(AuthenticationManager authenticationManager, String jwtSecret) { this.authenticationManager = authenticationManager; this.jwtSecret = jwtSecret; setFilterProcessesUrl("/api/authenticate"); } @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) { try { LoginRequest loginRequest = new ObjectMapper().readValue(request.getInputStream(), LoginRequest.class); Authentication authentication = new UsernamePasswordAuthenticationToken( loginRequest.getUsername(), loginRequest.getPassword() ); return authenticationManager.authenticate(authentication); } catch (IOException e) { throw new RuntimeException(e); } } @Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) { UserPrincipal userPrincipal = (UserPrincipal) authResult.getPrincipal(); String token = Jwts.builder() .setSubject(userPrincipal.getUsername()) .setIssuedAt(new Date()) .setExpiration(new Date(System.currentTimeMillis() + 864000000)) .signWith(SignatureAlgorithm.HS512, jwtSecret) .compact(); response.addHeader("Authorization", "Bearer " + token); }}
JwtAuthenticationFilter
類繼承了UsernamePasswordAuthenticationFilter
類,它用于處理基于用戶名和密碼的身份驗證。它還使用AuthenticationManager
來驗證用戶名和密碼是否正確。jwtSecret
在構(gòu)造函數(shù)中被注入,用于生成JWT令牌。
在attemptAuthentication()
方法中,LoginRequest
對象被反序列化為從請求中獲取的用戶名和密碼。這些值被封裝到UsernamePasswordAuthenticationToken
中,并傳遞給AuthenticationManager
以驗證用戶身份。
在身份驗證成功后,successfulAuthentication()
方法被調(diào)用。在這里,UserPrincipal
對象被從Authentication
對象中獲取,然后使用Jwts
類生成JWT令牌。setSubject()
方法將用戶名設(shè)置為JWT主題。setIssuedAt()
方法設(shè)置JWT令牌的發(fā)行時間。setExpiration()
方法設(shè)置JWT令牌的到期時間。signWith()
方法使用HS512算法和jwtSecret
密鑰對JWT令牌進行簽名。最后,JWT令牌被添加到響應(yīng)標(biāo)頭中。
下面是JwtAuthorizationFilter
的實現(xiàn):
public class JwtAuthorizationFilter extends BasicAuthenticationFilter { private final String jwtSecret; public JwtAuthorizationFilter(AuthenticationManager authenticationManager, String jwtSecret) { super(authenticationManager); this.jwtSecret = jwtSecret; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { String authorizationHeader = request.getHeader("Authorization"); if (authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) { chain.doFilter(request, response); return; } String token = authorizationHeader.replace("Bearer ", ""); try { Jws claimsJws = Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token); String username = claimsJws.getBody().getSubject(); List authorities = (List) claimsJws.getBody().get("authorities"); List grantedAuthorities = authorities.stream() .map(SimpleGrantedAuthority::new) .collect(Collectors.toList()); Authentication authentication = new UsernamePasswordAuthenticationToken(username, null, grantedAuthorities); SecurityContextHolder.getContext().setAuthentication(authentication); chain.doFilter(request, response); } catch (JwtException e) { response.setStatus(HttpStatus.UNAUTHORIZED.value()); } }}
JwtAuthorizationFilter
類繼承了BasicAuthenticationFilter
類,并覆蓋了doFilterInternal()
方法。在這個方法中,請求頭中的Authorization
標(biāo)頭被解析,如果它不是以Bearer
開頭,則直接傳遞給過濾器鏈。否則,從令牌中解析出主題(用戶名)和授權(quán)信息,然后創(chuàng)建一個包含用戶身份驗證和授權(quán)信息的Authentication
對象,并將其設(shè)置到SecurityContextHolder
中。
如果JWT令牌無效,JwtException
將被拋出,并返回HTTP 401未經(jīng)授權(quán)的錯誤。
關(guān)鍵詞:
2023-04-07 01:23:20
2023-04-06 21:55:36
2023-04-06 20:49:17
2023-04-06 19:32:16
2023-04-06 17:38:25
2023-04-06 16:49:11
2023-04-06 15:58:29
2023-04-06 13:56:30
2023-04-06 12:46:17
2023-04-06 11:55:15
2023-04-06 11:05:25
2023-04-06 10:07:51
2023-04-06 09:24:07
2023-04-06 08:32:46
2023-04-06 07:11:01
2023-04-06 04:56:19
2023-04-05 23:45:48
2023-04-05 19:37:56
2023-04-05 18:01:11
資訊
品牌
24小時熱點