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日

相关文章

  • Spring和SpringMVC扫描注解类冲突的解决方案

    在Spring和SpringMVC中,都有扫描注解类的功能。但是,如果在两个框架中同时使用了相同的注解类,就会出现冲突。本文将详细讲解Spring和SpringMVC扫描注解类冲突的解决方案,并提供两个示例说明。 解决方案一:使用不同的包名 我们可以在Spring和SpringMVC中使用不同的包名,来避免扫描相同的注解类。下面是一个示例: // Sprin…

    Java 2023年5月18日
    00
  • Javafx简单实现【我的电脑资源管理器】效果

    下面是详细讲解“Javafx简单实现【我的电脑资源管理器】效果”的完整攻略: 1. 准备工作 在进行Javafx开发前,我们需要先安装好开发所需的环境,包括JDK和IDE。这里我们选择JDK1.8和Intellij IDEA作为开发环境。具体安装方法这里不作过多说明,可以参考官方文档进行安装。 2. 建立项目 使用Intellij IDEA创建一个新的Jav…

    Java 2023年5月24日
    00
  • java开发web前端cookie session及token会话机制详解

    Java开发Web前端Cookie、Session及Token会话机制详解 在Web开发中,为了维护用户的登录状态、保护用户信息的安全,常常使用Cookie、Session、Token等会话机制。本文将详细讲解这三种机制的原理、用法和应用场景。 Cookie 什么是Cookie Cookie是一种用于保存客户端状态的机制。Web服务器在HTTP响应头中添加S…

    Java 2023年5月20日
    00
  • springBoot项目常用目录解读

    下面是对“springBoot项目常用目录解读”的详细讲解: 1. 项目结构概述 在理解Spring Boot项目的目录结构之前,需要首先理解Spring Boot的项目结构。Spring Boot的项目结构与标准的Maven或Gradle项目结构类似,区别在于一个主要目录——src/main,这个目录中分别包含了几个子目录,如下: src/main/jav…

    Java 2023年5月19日
    00
  • SpringMVC上传文件FileUpload使用方法详解

    下面是详细讲解“SpringMVC上传文件FileUpload使用方法详解”的完整攻略: 什么是SpringMVC文件上传? SpringMVC文件上传就是通过SpringMVC框架提供的功能,实现将文件从客户端传输到服务器端并存储到指定位置的过程。文件上传是Web应用程序经常使用的功能之一。通过SpringMVC文件上传,我们可以轻松地完成文件上传的处理,…

    Java 2023年6月15日
    00
  • 关于@JsonProperty和@JSONField注解的区别及用法

    下面就是关于JsonProperty和JSONField注解的区别及用法的完整攻略。 1. 什么是 @JsonProperty 和 @JSONField 注解 @JsonProperty 和 @JSONField 都是用来指定属性名与 Json 中的名称对应关系的注解,但是前者是 Jackson 库中提供的注解,后者是阿里巴巴 fastjson 库中提供的注…

    Java 2023年5月26日
    00
  • Mybatis学习笔记之动态SQL揭秘

    Mybatis 是一种流行的持久化框架,其核心是SQL映射文件。动态SQL是Mybatis的重要功能之一,可以帮助开发人员解决复杂的SQL语句拼接问题,从而提高开发速度和可维护性。本文将为您详细讲解Mybatis动态SQL的使用方法和技巧。 什么是动态SQL Mybatis的SQL语句是通过XML文件进行配置的,因此可以灵活地进行动态SQL语句的拼接。动态S…

    Java 2023年6月1日
    00
  • jdbc操作mysql数据库实例

    一、前言 Java Database Connectivity(JDBC)是Java提供的一种操作各种关系型数据库的API。本攻略将介绍如何使用JDBC操作MySQL数据库,包括连接MySQL数据库、创建表、插入记录、查询记录、更新记录和删除记录等操作。 二、依赖和环境需求 在开始实践之前,我们需要准备以下依赖和环境: MySQL数据库(可以是本地安装的,也…

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