springsecurity 如何实现前后端分离登录跳转

在前后端分离的应用中,通常前端和后端是通过 API 接口进行通信的,前端负责用户界面和用户交互,而后端处理业务逻辑和数据存取。在这种架构下,登录认证流程需要特别设计,以确保安全性并提供良好的用户体验。以下是如何在 Spring Security 中实现前后端分离登录和跳转的详细步骤:

### 1. **配置 Spring Security**

首先,你需要配置 Spring Security,以便它可以处理基于 API 的认证请求而不是传统的表单登录。

**步骤:**

1. **配置 `SecurityConfig`**   ```java
   

import org.springframework.context.annotation.Bean;
   import org.springframework.context.annotation.Configuration;
   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.web.authentication.UsernamePasswordAuthenticationFilter;
   import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
   import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
   import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
   import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

   @Configuration
   @EnableWebSecurity
   public class SecurityConfig extends WebSecurityConfigurerAdapter {

       @Override
       protected void configure(HttpSecurity http) throws Exception {
           http
               .csrf().disable() // Disable CSRF for API
               .authorizeRequests()
               .antMatchers("/api/auth/**").permitAll() // Allow unauthenticated access to auth endpoints
               .anyRequest().authenticated()
               .and()
               .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
               .and()
               .addFilterBefore(authenticationFilter(), UsernamePasswordAuthenticationFilter.class);
       }

       @Bean
       public AuthenticationFilter authenticationFilter() throws Exception {
           AuthenticationFilter filter = new AuthenticationFilter();
           filter.setAuthenticationManager(authenticationManager());
           return filter;
       }
   }


   ```

2. **创建自定义认证过滤器**

   由于我们使用前后端分离,认证通常通过 REST API 完成,所以我们需要自定义 `UsernamePasswordAuthenticationFilter`。   ```java
   

import com.fasterxml.jackson.databind.ObjectMapper;
   import org.springframework.security.core.Authentication;
   import org.springframework.security.core.AuthenticationException;
   import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

   import javax.servlet.http.HttpServletRequest;
   import javax.servlet.http.HttpServletResponse;
   import java.io.IOException;

   public class AuthenticationFilter extends UsernamePasswordAuthenticationFilter {

       @Override
       public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
           try {
               ObjectMapper mapper = new ObjectMapper();
               LoginRequest loginRequest = mapper.readValue(request.getInputStream(), LoginRequest.class);
               return getAuthenticationManager().authenticate(
                   new UsernamePasswordAuthenticationToken(loginRequest.getUsername(), loginRequest.getPassword())
               );
           } catch (IOException e) {
               throw new RuntimeException(e);
           }
       }

       @Override
       protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
           // Return a success response to the frontend
           response.setStatus(HttpServletResponse.SC_OK);
           response.getWriter().write("Authentication successful");
       }

       @Override
       protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
           // Return a failure response to the frontend
           response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
           response.getWriter().write("Authentication failed");
       }
   }


   ```

3. **实现登录请求和响应**

   在自定义过滤器中,你可以控制成功和失败的响应。你可以根据需要修改成功和失败的处理方式,比如返回 JWT token。

### 2. **前端实现**

前端部分负责发送登录请求并处理响应。通常使用 AJAX 或 Fetch API 发送请求。

**步骤:**

1. **发送登录请求**

   使用 JavaScript 发送 POST 请求到后端登录接口。   ```javascript

   async function login(username, password) {
       try {
           let response = await fetch('http://localhost:8080/api/auth/login', {
               method: 'POST',
               headers: {
                   'Content-Type': 'application/json'
               },
               body: JSON.stringify({ username, password })
           });

           if (response.ok) {
               // Handle success
               let result = await response.text();
               console.log(result); // Handle result, e.g., redirect to a different page or store token
           } else {
               // Handle error
               let error = await response.text();
               console.error('Authentication failed:', error);
           }
       } catch (error) {
           console.error('Error during authentication:', error);
       }
   }


   ```

2. **处理成功和失败**

   在处理登录结果时,可以根据需要进行页面重定向、存储 JWT token 等操作。

   ```javascript

   if (response.ok) {
       // Redirect to the main page or store the token
       window.location.href = '/home';
   } else {
       alert('Login failed');
   }


   ```

### 3. **处理 Token**

如果使用 JWT 或其他 token 机制进行认证,可以在用户登录成功后,将 token 存储在浏览器中(例如在 Local Storage 或 Cookies 中),并在后续的 API 请求中将 token 附加到请求头中。

**步骤:**

1. **存储 Token**   ```javascript

   async function login(username, password) {
       try {
           let response = await fetch('http://localhost:8080/api/auth/login', {
               method: 'POST',
               headers: {
                   'Content-Type': 'application/json'
               },
               body: JSON.stringify({ username, password })
           });

           if (response.ok) {
               let result = await response.json();
               localStorage.setItem('authToken', result.token); // Save token to local storage
               window.location.href = '/home';
           } else {
               alert('Login failed');
           }
       } catch (error) {
           console.error('Error during authentication:', error);
       }
   }


   ```

2. **在 API 请求中附加 Token**   ```javascript
   

async function fetchProtectedResource() {
       const token = localStorage.getItem('authToken');
       try {
           let response = await fetch('http://localhost:8080/api/protected', {
               method: 'GET',
               headers: {
                   'Authorization': `Bearer ${token}`
               }
           });

           if (response.ok) {
               let data = await response.json();
               console.log('Protected data:', data);
           } else {
               console.error('Failed to fetch protected resource');
           }
       } catch (error) {
           console.error('Error fetching protected resource:', error);
       }
   }


   ```

### 4. **后端实现 Token 验证**

如果使用 JWT,确保后端能够验证 token 并解析用户信息。

**步骤:**

1. **创建 JWT 过滤器**   ```java
 

 import io.jsonwebtoken.Claims;
   import io.jsonwebtoken.Jwts;
   import io.jsonwebtoken.SignatureAlgorithm;
   import org.springframework.security.core.context.SecurityContextHolder;
   import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

   import javax.servlet.FilterChain;
   import javax.servlet.ServletException;
   import javax.servlet.http.HttpServletRequest;
   import javax.servlet.http.HttpServletResponse;
   import java.io.IOException;

   public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

       private final String secretKey = "your_secret_key";

       @Override
       protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
           // Generate a JWT token
           String token = Jwts.builder()
               .setSubject(authResult.getName())
               .signWith(SignatureAlgorithm.HS512, secretKey.getBytes())
               .compact();
           response.setHeader("Authorization", "Bearer " + token);
       }

       @Override
       protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
           response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
           response.getWriter().write("Authentication failed");
       }
   }


   ```

2. **验证 Token**

   在配置中添加 JWT 过滤器以验证请求中的 token。   ```java

   @Configuration
   @EnableWebSecurity
   public class SecurityConfig extends WebSecurityConfigurerAdapter {

       @Override
       protected void configure(HttpSecurity http) throws Exception {
           http
               .csrf().disable()
               .authorizeRequests()
               .antMatchers("/api/auth/**").permitAll()
               .anyRequest().authenticated()
               .and()
               .addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
               .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
       }
   }
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值