首页> Java> java教程> 正文

使用 Spring Security 保护微服务:实施 JWT

PHPz
发布: 2024-08-24 06:42:38
原创
326 人浏览过

JSON 网络令牌 (JWT)

JWT(JSON Web Token)是一种以 JSON 对象的形式在两方(例如客户端和服务器)之间安全传输信息的方法。它的设计紧凑且 URL 安全,可以轻松地在 URL、标头中传递。

  1. 标题
  2. 有效负载
  3. 签名

标题
标头通常由两部分组成:令牌的类型 (JWT) 和所使用的签名算法,例如 HMAC SHA256 或 RSA。

{
"alg":"HS256",
“类型”:“JWT”
}

有效负载
这是实际数据存储的地方。它可以包含用户 ID、角色、过期时间和其他声明(有关用户或会话的数据)等信息。

{
“电子邮件”:“ayushstwt@gmail.com”,
“名字”:“阿尤什”
}

签名
确保令牌的完整性。这是一项安全功能,可确保令牌不被更改。它是通过使用指定算法将编码的标头和有效负载与密钥组合起来创建的。签名帮助服务器验证token是否合法且未被篡改。

智威汤逊的优势

无需重复发送凭据:使用 JWT,您不必在每次请求时都发送您的用户名和密码。相反,您登录一次,服务器就会给您一个令牌。然后,您可以在每个请求中发送此令牌来证明您的身份,从而使该过程更加安全和高效。

内置过期时间:每个 JWT 都有一个过期时间,这意味着它仅在特定期限内有效。如果令牌以某种方式被拦截,这可以降低长期滥用的风险。过期后,用户需要重新登录才能获取新的令牌,增加了额外的安全层。

JWT 与 Spring Boot 通过在登录后颁发令牌来安全地管理用户身份验证。这些令牌随每个请求一起发送,确保安全、无状态的通信,而无需重复发送凭据。

无状态通信意味着服务器不记得过去的请求。每个请求都携带所需的一切(如 JWT),因此服务器不存储会话信息。

在 Java Spring Boot 应用程序中实现 JWT 涉及几个步骤。以下是帮助您入门的简化大纲:

1.添加依赖项

在 pom.xml 文件中包含必要的依赖项

雷雷

我们使用 JWT 创建 spring-boot 应用程序所需的所有依赖项

雷雷

我们正在使用不同类型的依赖项,例如

  • Spring Boot Starter Actuator:3.3.3 - 添加生产就绪功能,例如监控和运行状况检查。
  • Spring Boot Starter Data JPA:3.3.3 - 通过 JPA 支持简化数据库交互。
  • Spring Boot Starter Security:3.3.3 - 提供身份验证和授权等安全功能。
  • Spring Boot Starter Web:3.3.3 - 支持构建 Web 应用程序,包括 RESTful 服务。
  • JJWT API:0.12.5 - 处理 JWT 创建和解析以实现安全令牌管理。
  • JJWT Impl:0.12.5 - 实现核心 JWT 功能。
  • JJWT Jackson:0.12.5 - 使用 Jackson 启用 JWT JSON 解析。
  • MySQL 连接器:运行时 - 将您的应用程序连接到 MySQL 数据库。
  • Lombok:未指定 - 通过注释减少样板代码。
  • Spring Boot Starter Test:3.3.3 - 为 Spring Boot 应用程序提供测试支持。
  • Spring 安全测试:3.3.3 - 帮助测试安全配置。
  • Spring Boot Starter 验证:3.3.3 - 添加对请求和响应对象的验证支持。
  • SpringDoc OpenAPI Starter WebMVC UI:2.5.0 - 集成了 API 文档的 Swagger UI。
  • ModelMapper:3.1.1 - 简化不同层之间的对象映射。

*2。项目结构*

Securing Microservices with Spring Security: Implementing JWT

3.在application.properties文件中添加配置

雷雷

4.创建 USER 实体

雷雷

** 5.创建服务和存储库类以及接口**

Repository.java

雷雷

service.java

雷雷

6。创建 DTO 用于登录、登录请求和响应

创建loginDTO.java

雷雷

loginResponse.java

雷雷

注册DTO.java

雷雷

RegisterResponse.java

雷雷

*7。为了从 API 发送自定义响应,我们使用 ResponseHandler.java *

package com.tier3Hub.user_auth_service.utils; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import java.util.HashMap; import java.util.Map; public class ResponseHandler { public static ResponseEntity generateResponse(String message, HttpStatus status, Object responseObj) { Map map = new HashMap(); map.put("message", message); map.put("status", status.value()); map.put("data", responseObj); return new ResponseEntity(map, status); } }
        
登录后复制

8. for storing some constants we create the class inside the utils package that is ApplicationConstants.java

package com.tier3Hub.user_auth_service.utils; public class AppConstants { public static final String[] PUBLIC_URLS = { "/v3/api-docs/**", "/swagger-ui/**", "/api/auth/register/**", "/api/auth/login/**","/api/auth/registerAdmin/**" }; }
登录后复制

9. for converting the object one to another we use the dependency that is model mapper for configuration that we create the class inside the config package that is ApplicationConfigs.java

package com.tier3Hub.user_auth_service.config; import org.modelmapper.ModelMapper; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class ApplicationConfigs { @Bean public ModelMapper modelMapper() { return new ModelMapper(); } }
登录后复制

**
This is the basic setup that we do for every spring-boot application we create now securing the rest endpoint with JWT we started.
**

now inside the security package we create the class called JWTFilter.java

The JWTFilter is a custom Spring Security filter that intercepts HTTP requests to validate JWTs. It checks for the "Authorization" header, extracts the token, and retrieves the username. If the token is valid, it creates an authentication token with user details and sets it in the security context, allowing the application to recognize the authenticated user for further processing.

package com.tier3Hub.user_auth_service.security; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; 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.core.userdetails.UserDetailsService; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.stereotype.Service; import org.springframework.web.filter.OncePerRequestFilter; import java.io.IOException; @Service public class JWTFilter extends OncePerRequestFilter { @Autowired private UserDetailsService userDetailsService; @Autowired private JWTUtil jwtUtil; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { 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) { UserDetails userDetails = userDetailsService.loadUserByUsername(username); if (jwtUtil.validateToken(jwt)) { UsernamePasswordAuthenticationToken auth = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); auth.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(auth); } } chain.doFilter(request, response); } }
登录后复制

create the class JWTUtil.java

The JWTUtil class manages JWT operations, including extracting usernames and expiration dates from tokens. It generates new tokens using a secret key and validates existing tokens by checking their expiration. The class uses HMAC for signing and includes methods to parse claims and determine if tokens are expired, ensuring secure authentication and authorization in the application.

package com.tier3Hub.user_auth_service.security; import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; import org.springframework.stereotype.Service; import javax.crypto.SecretKey; import java.util.Date; import java.util.HashMap; import java.util.Map; @Service public class JWTUtil { private String SECRET_KEY = "TaK+HaV^uvCHEFsEVfypW#7g9^k*Z8$V"; private SecretKey getSigningKey() { return Keys.hmacShaKeyFor(SECRET_KEY.getBytes()); } public String extractUsername(String token) { Claims claims = extractAllClaims(token); return claims.getSubject(); } public Date extractExpiration(String token) { return extractAllClaims(token).getExpiration(); } private Claims extractAllClaims(String token) { return Jwts.parser() .verifyWith(getSigningKey()) .build() .parseSignedClaims(token) .getPayload(); } private Boolean isTokenExpired(String token) { return extractExpiration(token).before(new Date()); } public String generateToken(String username) { Map claims = new HashMap<>(); return createToken(claims, username); } private String createToken(Map claims, String subject) { return Jwts.builder() .claims(claims) .subject(subject) .header().empty().add("typ","JWT") .and() .issuedAt(new Date(System.currentTimeMillis())) .expiration(new Date(System.currentTimeMillis() + 1000 * 60 * 50)) // 5 minutes expiration time .signWith(getSigningKey()) .compact(); } public Boolean validateToken(String token) { return !isTokenExpired(token); } }
登录后复制

*configure the Spring security and add some modifictaion we create the class SecurityConfig.java *

The SecurityConfig class sets up security for the application using Spring Security. It defines access rules, allowing public endpoints while restricting others based on user roles. The class incorporates a JWT filter to validate tokens and uses BCrypt for password encoding. It also configures an authentication manager with a custom user details service for secure user authentication.

package com.tier3Hub.user_auth_service.config; import com.tier3Hub.user_auth_service.security.JWTFilter; import com.tier3Hub.user_auth_service.service.UserInfoConfigManager; import com.tier3Hub.user_auth_service.utils.AppConstants; 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.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; 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.configurers.AbstractHttpConfigurer; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @Configuration @EnableWebSecurity public class SecurityConfig { @Autowired private JWTFilter jwtFilter; @Autowired private UserInfoConfigManager userInfoConfigManager; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { return http.authorizeHttpRequests(request -> request .requestMatchers(AppConstants.PUBLIC_URLS).permitAll() .requestMatchers("/api/test/public/hello/**").hasAnyRole("USER","ADMIN") .requestMatchers("/api/test/private/**").hasRole("ADMIN") .anyRequest() .authenticated()) .csrf(AbstractHttpConfigurer::disable) .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class) .build(); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userInfoConfigManager).passwordEncoder(passwordEncoder()); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration auth) throws Exception { return auth.getAuthenticationManager(); } }
登录后复制

The securityFilterChain method configures access rules for different API endpoints in the Spring application. It permits public URLs and applies role-based access control for user and admin roles. Role-based authentication restricts resource access based on user roles (e.g., USER, ADMIN). In Spring Boot, you define roles and configure security settings in the SecurityConfig class to specify access permissions. During user registration, assign roles, and use annotations like @PreAuthorize to enforce role checks in controllers. This approach enhances security, allows easy permission management, and simplifies user access rights as the application scales. Implementing role-based auth provides flexibility and maintainability for your user management system. CSRF protection is disabled, and a custom JWT filter is added to authenticate requests based on JSON Web Tokens, ensuring secure and controlled access to resources.

configureGlobal method handle configures global authentication settings in a Spring application. It uses a custom user details service for loading user data and a BCrypt password encoder for secure password hashing. Additionally, it provides an AuthenticationManager bean for handling authentication processes, ensuring a secure and efficient user authentication system that leverages strong password management practices.

create the endpoints for register and login

package com.tier3Hub.user_auth_service.Controller; import com.tier3Hub.user_auth_service.dto.LoginDTO; import com.tier3Hub.user_auth_service.dto.LoginResponse; import com.tier3Hub.user_auth_service.dto.RegisterDTO; import com.tier3Hub.user_auth_service.security.JWTUtil; import com.tier3Hub.user_auth_service.service.AuthService; import com.tier3Hub.user_auth_service.service.UserInfoConfigManager; import com.tier3Hub.user_auth_service.utils.ResponseHandler; import jakarta.validation.Valid; 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.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.userdetails.UserDetails; 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; @RestController @RequestMapping("/api/auth") public class AuthController { @Autowired JWTUtil jwtUtil; @Autowired AuthService authService; @Autowired AuthenticationManager authenticationManager; @Autowired private UserInfoConfigManager userInfoConfigManager; @PostMapping("/register") public ResponseEntity register(@Valid @RequestBody RegisterDTO registerDTO) { return ResponseHandler.generateResponse("User registered successfully", HttpStatus.OK, authService.register(registerDTO)); } @PostMapping("/login") public ResponseEntity login(@Valid @RequestBody LoginDTO loginDTO) { try { Authentication authenticate = authenticationManager .authenticate(new UsernamePasswordAuthenticationToken(loginDTO.getUsername(), loginDTO.getPassword())); UserDetails userDetails = userInfoConfigManager.loadUserByUsername(loginDTO.getUsername()); String jwt = jwtUtil.generateToken(userDetails.getUsername()); LoginResponse loginResponse = LoginResponse .builder() .accessToken(jwt) .build(); return ResponseHandler.generateResponse("User logged in successfully", HttpStatus.OK, loginResponse); } catch (Exception e) { return new ResponseEntity<>("Incorrect username or password", HttpStatus.BAD_REQUEST); } } }
        
登录后复制

This login method in the AuthController handles user login requests. It takes a LoginDTO containing the username and password, validates them, and attempts authentication using the AuthenticationManager. Upon successful authentication, it retrieves user details and generates a JWT token using the JWTUtil class. The token is then included in a LoginResponse object and returned with a success message. If authentication fails, it catches the exception and returns a "Incorrect username or password" response with a 400 status code.

generateToken(String username):This method creates an empty claims map and calls the createToken method with the username as the subject. It serves as the entry point for token generation.

c*reateToken(Map claims, String subject):* This method builds the JWT using the Jwts.builder(). It sets the claims, subject, and token metadata, such as issue date and expiration time (set to 5 minutes). The token is then signed with a secret key and compacted into a string format for transmission.

Testing

now we run the application

Securing Microservices with Spring Security: Implementing JWT

and hit the URL here our application is runing on 8000 port

http://localhost:8000/swagger-ui/index.html

在專案中使用 Swagger 可以增強 API 文件和測試。它為開發人員提供了一個用戶友好的介面,以探索您的 API、了解請求/回應結構並直接從文件測試端點。透過整合Swagger,您可以根據程式碼註釋自動產生API文檔,使前端和後端開發人員更輕鬆地高效協作。

Securing Microservices with Spring Security: Implementing JWT

首先我們註冊用戶

Securing Microservices with Spring Security: Implementing JWT

我們得到這樣的回應

Securing Microservices with Spring Security: Implementing JWT

之後我們登入使用者

Securing Microservices with Spring Security: Implementing JWT

我們得到這樣的回應

Securing Microservices with Spring Security: Implementing JWT

結論

該專案在 Spring Boot 應用程式中使用 JWT(JSON Web Tokens)實現基於角色的身份驗證。它具有安全的身份驗證機制,使用者可以註冊和登錄,接收根據分配的角色(例如 USER 或 ADMIN)授予存取權限的 JWT。 SecurityConfig 類別設定存取權限,確保每個人都可以存取公共端點,同時將敏感操作僅限於授權使用者。 JWTUtil 類別處理令牌建立、驗證和使用者提取。總體而言,此設定增強了安全性,實現了跨應用程式的無縫且強大的存取控制。

該專案採用了一個全面的安全框架,利用 Spring Security 進行使用者身份驗證和授權。 AuthController 方便用戶註冊和登錄,在身份驗證成功後產生 JWT。該應用程式使用 JWTFilter 攔截請求並驗證令牌,確保只有經過身份驗證的使用者才能存取受保護的資源。透過整合基於角色的存取控制,該專案提供了靈活且安全的使用者管理系統。這種設計不僅提高了安全性,還透過最大限度地減少重複登入的需要來增強使用者體驗。總的來說,它為建立可擴展且安全的微服務奠定了堅實的基礎。

您可以在我的 GitHub 儲存庫上探索使用者驗證服務的完整原始程式碼。該專案展示了各種功能,例如用戶註冊、登入和使用 JWT 進行身份驗證的安全存取。請隨意查看、貢獻或將其用作您自己的專案的參考!

GitHub 儲存庫:https://github.com/ishrivasayush/user-auth-service

對於有興趣深入研究 JSON Web Tokens (JWT) 的人,我建議參觀 jwt.io。該資源提供了有關 JWT 的全面信息,包括其工作原理、結構和實際示例。這是理解基於令牌的身份驗證和授權的一個很好的起點,這對於現代 Web 應用程式至關重要。無論您是初學者還是想要刷新知識,jwt.io 都提供了有關安全管理使用者會話的寶貴見解。

以上是使用 Spring Security 保护微服务:实施 JWT的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责声明 Sitemap
PHP中文网:公益在线PHP培训,帮助PHP学习者快速成长!