Sping Security前后端分离两种实战方案

下面我将详细讲解“Sping Security前后端分离两种实战方案”的完整攻略。

方案概述

Spring Security作为一个强大的安全框架,在项目中得到了广泛的应用,但是其安全配置可能会随着项目的复杂度而变得非常繁琐。而前后端分离的架构模式也越来越多地被应用在实际项目中,那么如何在Spring Security中实现前后端分离呢?本文将介绍两种前后端分离的实战方案。

方案一:基于JWT的前后端分离

准备工作

在开始前,需要先明确一下如何在Spring Security中使用JWT:

  1. 引入依赖:

xml
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>

  1. 实现JWT工具类:

```java
public class JwtUtil {

   private static final String SECRET = "mySecret";

   public static String generateToken(UserDetails userDetails) {
       Date now = new Date();
       Date expiryDate = new Date(now.getTime() + 3600000);
       return Jwts.builder()
               .setSubject(userDetails.getUsername())
               .setIssuedAt(now)
               .setExpiration(expiryDate)
               .signWith(SignatureAlgorithm.HS512, SECRET)
               .compact();
   }

   public static String getUsernameFromToken(String token) {
       return Jwts.parser()
               .setSigningKey(SECRET)
               .parseClaimsJws(token)
               .getBody()
               .getSubject();
   }

   public static boolean validateToken(String token, UserDetails userDetails) {
       String username = getUsernameFromToken(token);
       return username.equals(userDetails.getUsername());
   }

}
```

其中,SECRET是自己定义的密钥,在实际项目中需要更改。

方案实现

在基于JWT的前后端分离中,需要实现如下步骤:

  1. 用户登录时,前端发送POST请求,携带用户名和密码。

javascript
axios.post('/login', {
username: this.username,
password: this.password
}).then(response => {
const token = response.data.token;
localStorage.setItem('token', token);
// ...
});

  1. 后端接收请求,进行登录验证,验证成功则生成JWT并作为响应返回给前端。

```java
public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

   @Override
   public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
       try {
           AuthenticationRequest authRequest = new ObjectMapper()
                   .readValue(request.getInputStream(), AuthenticationRequest.class);
           UsernamePasswordAuthenticationToken authenticationToken =
                   new UsernamePasswordAuthenticationToken(authRequest.getUsername(), authRequest.getPassword(), Collections.emptyList());
           return authenticationManager.authenticate(authenticationToken);
       } catch (IOException e) {
           throw new RuntimeException(e);
       }
   }

   @Override
   protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
       UserDetails userDetails = (UserDetails) authResult.getPrincipal();
       String token = JwtUtil.generateToken(userDetails);
       response.addHeader("Authorization", "Bearer " + token);
   }

}
```

其中,AuthenticationRequest是一个POJO,用于接收前端请求中的用户名和密码。

  1. 前端保存JWT,并在每次请求时携带JWT。

javascript
const token = localStorage.getItem('token');
axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;

  1. 后端实现JWT的校验,在校验失败时返回401状态码。

```java
public class JwtAuthorizationFilter extends BasicAuthenticationFilter {

   @Override
   protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
       String header = request.getHeader("Authorization");
       if (header == null || !header.startsWith("Bearer ")) {
           chain.doFilter(request, response);
           return;
       }
       UsernamePasswordAuthenticationToken authenticationToken = getAuthentication(request);
       SecurityContextHolder.getContext().setAuthentication(authenticationToken);
       chain.doFilter(request, response);
   }

   private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
       String token = request.getHeader("Authorization").replace("Bearer ", "");
       if (JwtUtil.getUsernameFromToken(token) != null) {
           UserDetails userDetails = userDetailsService.loadUserByUsername(JwtUtil.getUsernameFromToken(token));
           if (JwtUtil.validateToken(token, userDetails)) {
               return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
           }
       }
       return null;
   }

}
```

示例一:基于Vue.js的前后端分离

以下是一个基于Vue.js的前后端分离示例,具体实现方式与上述方案实现步骤相同。前端使用了Vue-router进行路由管理,使用了Vue-cookie进行Cookie操作:

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'
import Login from '../views/Login.vue'
import axios from 'axios'
import VueCookies from 'vue-cookies'

Vue.use(VueRouter)
Vue.use(VueCookies)

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/login',
    name: 'Login',
    component: Login
  }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

router.beforeEach(function (to, from, next) {
  const token = Vue.$cookies.get('token');
  if (to.name !== 'Login' && !token) {
    next({ name: 'Login' });
  } else {
    next();
  }
});

axios.interceptors.request.use(function (config) {
  const token = Vue.$cookies.get('token');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

export default router

示例二:基于React的前后端分离

以下是一个基于React的前后端分离示例,具体实现方式与上述方案实现步骤相同。前端使用了React-router进行路由管理,使用了Js-cookie进行Cookie操作:

import React from 'react';
import { Route, Redirect } from 'react-router-dom';
import Cookie from 'js-cookie';

function PrivateRoute({ component: Component, ...rest }) {
  return (
    <Route
      {...rest}
      render={props =>
        Cookie.get('token') ? (
          <Component {...props} />
        ) : (
          <Redirect
            to={{
              pathname: '/login',
              state: { from: props.location }
            }}
          />
        )
      }
    />
  );
}

export default PrivateRoute;

方案二:基于Session的前后端分离

准备工作

在开始前,需要先明确一下如何在Spring Security中使用Session:

  1. 引入依赖:

xml
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-core</artifactId>
<version>1.3.3.RELEASE</version>
</dependency>

  1. 配置Spring Session,使用Redis作为Session存储:

```java
@Configuration
@EnableRedisHttpSession
public class HttpSessionConfig {

   @Bean
   public LettuceConnectionFactory connectionFactory() {
       return new LettuceConnectionFactory();
   }

}
```

方案实现

在基于Session的前后端分离中,需要实现如下步骤:

  1. 用户登录成功后,后端生成Session,并返回JSESSIONID给前端。

```java
public class SessionAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

   @Override
   public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
       HttpSession session = request.getSession();
       session.setAttribute("user", authentication.getPrincipal());
       response.getWriter().write(session.getId());
   }

}
```

  1. 前端保存JSESSIONID,并在每次请求时携带JSESSIONID。

javascript
const sessionId = localStorage.getItem('sessionId');
axios.defaults.headers.common['Cookie'] = `JSESSIONID=${sessionId}`;

  1. 后端根据JSESSIONID查找Session,并进行验证,验证成功则放行请求。

```java
public class SessionFilter extends GenericFilterBean {

   private final SessionRegistry sessionRegistry;

   public SessionFilter(SessionRegistry sessionRegistry) {
       this.sessionRegistry = sessionRegistry;
   }

   @Override
   public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
       if (request instanceof HttpServletRequest) {
           HttpSession session = ((HttpServletRequest) request).getSession(false);
           if (session != null) {
               UserDetails userDetails = (UserDetails) session.getAttribute("user");
               if (userDetails != null) {
                   SessionInformation sessionInformation = sessionRegistry.getSessionInformation(session.getId());
                   if (sessionInformation != null) {
                       if (sessionInformation.isExpired()) {
                           sessionRegistry.removeSessionInformation(session.getId());
                       } else {
                           SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()));
                       }
                   }
               }
           }
       }
       chain.doFilter(request, response);
   }

}
```

示例三:基于Angular的前后端分离

以下是一个基于Angular的前后端分离示例,具体实现方式与上述方案实现步骤相同。前端使用了Angular Router进行路由管理,使用了ngx-cookie-service进行Cookie操作:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { LoginComponent } from './login/login.component';
import { HomeComponent } from './home/home.component';
import { AuthGuard } from './auth.guard';
import { CookieService } from 'ngx-cookie-service';

const routes: Routes = [
  { path: 'login', component: LoginComponent },
  { path: '', component: HomeComponent, canActivate: [AuthGuard] }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {
  constructor(private cookieService: CookieService) {
    const sessionId = this.cookieService.get('JSESSIONID');
    document.cookie = 'JSESSIONID=' + sessionId;
  }
}

总结

本文介绍了两种基于Spring Security的前后端分离实现方案,基于JWT和基于Session。在使用前后端分离的架构模式时,只需要根据具体的项目需要选择合适的方案进行实现即可。在实际项目中,需要注意安全性和稳定性,并且需要注重实际应用效果的测试,以保证系统的稳定性和安全性。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Sping Security前后端分离两种实战方案 - Python技术站

(0)
上一篇 2023年5月20日
下一篇 2023年5月20日

相关文章

  • Linux CentOS服务器搭建与初始化配置教程

    让我详细讲解一下“Linux CentOS服务器搭建与初始化配置教程”的完整攻略。以下是整个过程的步骤和详细说明: 步骤一:安装CentOS系统 在服务器上插入CentOS的安装光盘或者USB启动盘,并按照引导安装系统。 在安装过程中需要选择安装的语言、时区等信息,可以根据需要进行设置。 分区时建议将/boot、/home、/var、/usr、/tmp、/ …

    Java 2023年6月15日
    00
  • log4j2的高并发死锁问题配置优化方式

    下面是关于“log4j2的高并发死锁问题配置优化方式”的完整攻略。 问题描述 在高并发场景下,log4j2可能会出现死锁问题。 这是由于log4j2的异步日志功能(Async Appenders)的工作方式导致的。Async Appenders需要将所有的日志事件都放在一个队列中,并在后台的线程中执行写入操作。但是当日志写入速度超过异步日志工作线程的处理速度…

    Java 2023年5月19日
    00
  • Java中线程安全有哪些实现思路

    Java中线程安全是多线程编程中非常重要的概念,因为线程安全的代码能够保证多个线程同时访问同一个共享变量时不会出现竞态条件等问题。下面是Java中线程安全的实现思路: 1. 使用synchronized关键字 synchronized是Java中最基本的实现线程安全的方式,用synchronized关键字修饰方法或代码块,表示只有一个线程可以进入该代码块或方…

    Java 2023年5月18日
    00
  • Struts中使用validate()输入校验方法详解

    关于“Struts中使用validate()输入校验方法详解”的完整攻略,下面是具体的内容: 1. 什么是validate()方法? 在Struts2中,validate()方法是对于输入进行校验的一种很重要的方式。在这个方法中,我们可以定义输入校验的方法,对于输入数据进行检查,如果不符合要求则返回一个错误信息,如果符合要求则不做处理。 2. validat…

    Java 2023年6月2日
    00
  • Java Runtime用法实战案例

    Java Runtime是Java语言提供的一个类库,位于java.lang包中,它提供了访问JVM进程的API,可以执行系统命令,启动新的进程等功能。 获取Runtime实例 Runtime runtime = Runtime.getRuntime(); 通过调用Runtime.getRuntime()方法可以获取当前Java虚拟机的Runtime实例。 …

    Java 2023年5月23日
    00
  • Java对象在JVM中的生命周期详解

    请听我一一讲解。 Java对象的生命周期 Java对象在JVM中的生命周期可以简单概括为以下四个阶段: 创建对象:当我们使用new关键字或者反射API创建对象时,JVM就会为对象分配内存空间,并调用构造函数进行对象的初始化。 使用对象:对象被创建出来后,我们可以调用它的各种方法对其进行一系列操作。 消亡对象:当对象不再被引用时,JVM就会自动回收它所占用的内…

    Java 2023年5月26日
    00
  • Oracle下的Java分页功能_动力节点Java学院整理

    Oracle下的Java分页功能_动力节点Java学院整理 在Web开发中,分页是非常常见的功能需求。本文将介绍如何在Oracle数据库中使用Java实现分页功能。 1. 实现思路 通过查询获取数据总数及相应的数据,计算出总页数,然后根据当前页大小和页码去查询相应的数据。 2. 具体实现 定义分页参数类PageInfo 我们定义一个分页参数类PageInfo…

    Java 2023年6月15日
    00
  • 使用JAVA实现http通信详解

    使用JAVA实现http通信可以通过以下几个步骤完成: 步骤1:引入相关包 在实现http通信之前,需要引入相关的包,这些包中包含了实现http通信所需要的类和方法。Java中实现http通信一般使用Apache提供的HttpComponents包,该包可以通过Maven引入,如下: <dependency> <groupId>org…

    Java 2023年5月18日
    00
合作推广
合作推广
分享本页
返回顶部