Spring Security 3 (JWT) + Google OAuth2 - 使用 MySQL 实体

问题描述 投票:0回答:0

我正在尝试创建一个适用于基本身份验证(Spring Security JWT,工作正常)和社交身份验证(谷歌)的应用程序,第一次使用 OAuth2,看起来后端工作正常,我能够使用登录基本身份验证(用户 + 密码)以及 OAuth2。调用端点时http://localhost:8080/oauth2/authorization/google我可以看到身份验证上下文来自 OAuth2 Principal,所以没关系。

我的问题在这里: 假设我想创建前端,例如使用 React 并访问安全端点。 如果我使用基本身份验证,那没关系,因为对 http://localhost:8080/api/v1/auth/authenticate(基本身份验证端点,而不是 oauth2)的 POST 调用返回我可以用来访问安全端点的 Bearer 令牌带有 **授权 **header 和 Bearer token。但是如果我使用 Google OAuth2,在 http://localhost:8080/oauth2/authorization/google 中成功登录到 google 后,我找不到任何令牌存储在 LocalStorage 中并在进一步的请求中使用它,但是在谷歌授权之后我仍然通过身份验证,如果我做更多请求我可以访问安全端点。

这是我的配置:

`@Table(name = "customer") 公共类客户实现 UserDetails {

private Long id;
private String email, password, username...;

}`

安全配置

public class SecurityConfiguration {

private final JwtAuthenticationFilter jwtAuthFilter;
private final AuthenticationProvider authenticationProvider;
private final UserService userService;

@Bean
public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception {
    return configuration.getAuthenticationManager();
}

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.csrf()
            .disable()
            .authorizeHttpRequests((authorize) -> authorize
                    .requestMatchers("/" ,"/api/v1/public/**", "/auth", "/logout", "/api/v1/auth/**", "/oauth/**", "/login", "/oauth2/**").permitAll()
                    .requestMatchers("/admin/**").hasRole("ADMIN")
                    .anyRequest().authenticated())
            .authenticationProvider(authenticationProvider)
            .logout(l -> l.logoutSuccessUrl("/").permitAll())
            .exceptionHandling()
            .authenticationEntryPoint((req, rsp, e) -> rsp.sendError(HttpServletResponse.SC_UNAUTHORIZED))
            .and()
            .sessionManagement()
            //.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
                .addFilterBefore(jwtAuthFilter, UsernamePasswordAuthenticationFilter.class)
                .formLogin().permitAll()
                .loginPage("/login")
                .successHandler(databaseAuthenticationSuccessHandler())
                .failureHandler((request, response, exception) -> response.setStatus(HttpStatus.UNAUTHORIZED.value()))
            .and()
            .logout().logoutSuccessUrl("/").permitAll()
            .and()
            .oauth2Login()
                .loginPage("/login")
                .defaultSuccessUrl("/api/v1/secured")
                .userInfoEndpoint()
                .and()
                .successHandler(oAuthAuthenticationSuccessHandler())
                .failureHandler((request, response, exception) -> response.setStatus(HttpStatus.UNAUTHORIZED.value()));
    return http.build();
}

@Bean
public AuthenticationSuccessHandler oAuthAuthenticationSuccessHandler() {
    return new OAuthSuccessHandler(userService);
}

@Bean
public AuthenticationSuccessHandler databaseAuthenticationSuccessHandler() {
    return new DatabaseLoginSuccessHandler(userService);
}

}

@Component
public class OAuthSuccessHandler implements AuthenticationSuccessHandler {
private final UserService userService;

@Autowired
public OAuthSuccessHandler(UserService userService) {
    this.userService = userService;
}

@Override
public void onAuthenticationSuccess(
        HttpServletRequest request,
        HttpServletResponse response,
        Authentication authentication
) throws IOException, ServletException {
    DefaultOidcUser user = (DefaultOidcUser) authentication.getPrincipal();
    userService.processOAuthLogin(user);
    response.sendRedirect("/api/v1/secured");
}

}

  public void processOAuthLogin(DefaultOidcUser user) {
    log.info("Attempting to login using Google OAuth2 for user: {}", user.getEmail());

    String email = user.getEmail();
    Optional<Customer> customerOpt = customerRepository.findByEmail(email);

    if(customerOpt.isPresent()) {
        Customer customer = customerOpt.get();
        if(customer.getProvider() != Provider.GOOGLE) {
            log.error("User with email: {} already exists in DB.", email);
            throw new IllegalArgumentException("An user already exists with that email, please log in using credentials");
        }
    } else {
        Customer customer = new Customer(user);
        customerRepository.save(customer);
    }
}

JwtAuthenticationFilter

@Component

@RequiredArgsConstructor 公共类 JwtAuthenticationFilter 扩展 OncePerRequestFilter {

private static final String AUTH_HEADER = "Authorization";
private static final String BEARER_TOKEN = "Bearer ";

private final JwtService jwtService;
private final UserService authService;

@Override
protected void doFilterInternal(
        HttpServletRequest request,
        HttpServletResponse response,
        FilterChain filterChain
) throws ServletException, IOException {
    final String authHeader = request.getHeader(AUTH_HEADER);

    if (request.getServletPath().contains("/api/v1/auth") || request.getServletPath().contains("/login")) {
        filterChain.doFilter(request, response);
        return;
    }

    if(authHeader == null || !authHeader.startsWith(BEARER_TOKEN)) {
        filterChain.doFilter(request, response);
        return;
    }

    String token = authHeader.substring(BEARER_TOKEN.length());
    String username = jwtService.extractUsername(token);

    if(username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
        UserDetails user = authService.loadUserByUsername(username);
        if(jwtService.isTokenValid(token, user)) {
            UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
                    user, null, user.getAuthorities()
            );
            authToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            SecurityContextHolder.getContext().setAuthentication(authToken);
        }
    }

    filterChain.doFilter(request, response);
}

}

java spring security oauth-2.0
© www.soinside.com 2019 - 2024. All rights reserved.