如何在Spring Boot的JWT认证项目中集成OAuth2
在现有JWT认证的Spring Boot房产项目中集成OAuth2支持
我正在开发一个房产列表的Spring Boot项目,已经基于JWT实现了认证机制并做了自定义扩展。现在希望集成OAuth2来提升安全性与灵活性,支持社交登录等OAuth2流程。以下是我的现有代码:
User实体类
package com.marvi.realestatelisting.Entity; import java.util.Collection; import java.util.List; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.Table; import lombok.Data; @Data @Entity @Table(name = "users") public class User implements UserDetails { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String email; private String password; private String role; @Override public String getUsername() { return email; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { return List.of(new SimpleGrantedAuthority(role)); } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } }
安全配置类
package com.marvi.realestatelisting.Config; 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.authentication.AuthenticationProvider; import org.springframework.security.authentication.dao.DaoAuthenticationProvider; 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.config.http.SessionCreationPolicy; 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; import com.marvi.realestatelisting.Service.UserService; @Configuration @EnableWebSecurity public class SecurityConfig { @Autowired private UserService userService; @Autowired private JWTAuthFilter jwtAuthFilter; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { httpSecurity.csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(request -> request .requestMatchers("/auth/**", "/public/**", "/uploads/**").permitAll() .requestMatchers("/agent/**").hasAuthority("AGENT") .requestMatchers("/user/**").hasAuthority("USER") .anyRequest().authenticated()) .sessionManagement(manager -> manager.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authenticationProvider(authenticationProvider()) .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class); return httpSecurity.build(); } @Bean public AuthenticationProvider authenticationProvider() { DaoAuthenticationProvider provider = new DaoAuthenticationProvider(); provider.setUserDetailsService(userService); provider.setPasswordEncoder(passwordEncoder()); return provider; } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration authenticationConfiguration) throws Exception { return authenticationConfiguration.getAuthenticationManager(); } }
JWT认证过滤器
package com.marvi.realestatelisting.Config; import java.io.IOException; 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 com.marvi.realestatelisting.Service.JwtUtils; import com.marvi.realestatelisting.Service.UserService; import jakarta.servlet.FilterChain; import jakarta.servlet.ServletException; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @Component public class JWTAuthFilter extends OncePerRequestFilter { @Autowired private JwtUtils jwtUtils; @Autowired private UserService userService; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { final String authHeader = request.getHeader("Authorization"); final String jwtToken; final String userEmail; if (authHeader == null || !authHeader.startsWith("Bearer ")) { filterChain.doFilter(request, response); return; } jwtToken = authHeader.substring(7); userEmail = jwtUtils.extractUsername(jwtToken); if (userEmail != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = userService.loadUserByUsername(userEmail); if (jwtUtils.isTokenValid(jwtToken, userDetails)) { UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authToken); } } filterChain.doFilter(request, response); } }
之前参考的YouTube教程没覆盖OAuth2集成内容,恳请指导如何在现有JWT认证架构中添加OAuth2支持。
解决方案:集成OAuth2与现有JWT架构
1. 添加依赖
在pom.xml中加入Spring Security OAuth2客户端与资源服务器依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-resource-server</artifactId> </dependency>
2. 扩展User实体
添加OAuth2相关字段,用于存储第三方登录的标识:
@Data @Entity @Table(name = "users") public class User implements UserDetails { // 原有字段... // OAuth2相关字段 private String oauth2Provider; private String oauth2ProviderId; // 构造方法、原有方法保持不变... }
3. 配置OAuth2客户端
在application.yml中配置社交登录提供商(以Google为例):
spring: security: oauth2: client: registration: google: client-id: YOUR_GOOGLE_CLIENT_ID client-secret: YOUR_GOOGLE_CLIENT_SECRET scope: profile, email provider: google: authorization-uri: https://accounts.google.com/o/oauth2/v2/auth token-uri: https://www.googleapis.com/oauth2/v4/token user-info-uri: https://www.googleapis.com/oauth2/v3/userinfo user-name-attribute: sub
4. 修改SecurityConfig
更新安全配置,添加OAuth2登录支持,并在登录成功后生成自定义JWT:
@Configuration @EnableWebSecurity public class SecurityConfig { @Autowired private UserService userService; @Autowired private JWTAuthFilter jwtAuthFilter; @Autowired private JwtUtils jwtUtils; @Bean public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception { httpSecurity.csrf(AbstractHttpConfigurer::disable) .authorizeHttpRequests(request -> request .requestMatchers("/auth/**", "/public/**", "/uploads/**", "/login/oauth2/code/**").permitAll() .requestMatchers("/agent/**").hasAuthority("AGENT") .requestMatchers("/user/**").hasAuthority("USER") .anyRequest().authenticated()) .sessionManagement(manager -> manager.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authenticationProvider(authenticationProvider()) .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class) // 添加OAuth2登录配置 .oauth2Login(oauth2 -> oauth2 .successHandler((request, response, authentication) -> { // 获取OAuth2用户信息 OAuth2User oAuth2User = (OAuth2User) authentication.getPrincipal(); String email = oAuth2User.getAttribute("email"); String provider = authentication.getAuthorities().stream() .filter(grantedAuthority -> grantedAuthority.getAuthority().startsWith("SCOPE_")) .findFirst() .map(grantedAuthority -> grantedAuthority.getAuthority().substring(6)) .orElse("google"); String providerId = oAuth2User.getAttribute("sub"); // 查找或创建本地用户 User user = userService.findOrCreateOAuth2User(email, provider, providerId); // 生成自定义JWT String jwtToken = jwtUtils.generateToken(user); // 返回JWT给前端(可根据需求调整返回方式,比如重定向或JSON响应) response.sendRedirect("/public/login-success?token=" + jwtToken); }) ); return httpSecurity.build(); } // 原有Bean保持不变... }
5. 扩展UserService
添加方法处理OAuth2用户的查找与创建:
@Service public class UserService implements UserDetailsService { @Autowired private UserRepository userRepository; @Autowired private PasswordEncoder passwordEncoder; // 原有方法... public User findOrCreateOAuth2User(String email, String provider, String providerId) { User user = userRepository.findByEmail(email); if (user == null) { user = new User(); user.setEmail(email); // OAuth2用户无需密码,设置随机加密值 user.setPassword(passwordEncoder.encode(UUID.randomUUID().toString())); user.setRole("USER"); // 默认角色,可按需调整 user.setOauth2Provider(provider); user.setOauth2ProviderId(providerId); userRepository.save(user); } return user; } }
6. 前端适配
前端添加OAuth2登录入口(如"用Google登录"按钮),点击后跳转至/oauth2/authorization/google,登录成功后从回调中获取JWT,后续请求沿用原有JWT认证流程即可。
内容的提问来源于stack exchange,提问作者Marvin Lewis
相关产品推荐
相关产品推荐

