下面我将详细讲解“Sping Security前后端分离两种实战方案”的完整攻略。
方案概述
Spring Security作为一个强大的安全框架,在项目中得到了广泛的应用,但是其安全配置可能会随着项目的复杂度而变得非常繁琐。而前后端分离的架构模式也越来越多地被应用在实际项目中,那么如何在Spring Security中实现前后端分离呢?本文将介绍两种前后端分离的实战方案。
方案一:基于JWT的前后端分离
准备工作
在开始前,需要先明确一下如何在Spring Security中使用JWT:
- 引入依赖:
xml
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
- 实现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的前后端分离中,需要实现如下步骤:
- 用户登录时,前端发送POST请求,携带用户名和密码。
javascript
axios.post('/login', {
username: this.username,
password: this.password
}).then(response => {
const token = response.data.token;
localStorage.setItem('token', token);
// ...
});
- 后端接收请求,进行登录验证,验证成功则生成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,用于接收前端请求中的用户名和密码。
- 前端保存JWT,并在每次请求时携带JWT。
javascript
const token = localStorage.getItem('token');
axios.defaults.headers.common['Authorization'] = `Bearer ${token}`;
- 后端实现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:
- 引入依赖:
xml
<dependency>
<groupId>org.springframework.session</groupId>
<artifactId>spring-session-core</artifactId>
<version>1.3.3.RELEASE</version>
</dependency>
- 配置Spring Session,使用Redis作为Session存储:
```java
@Configuration
@EnableRedisHttpSession
public class HttpSessionConfig {
@Bean
public LettuceConnectionFactory connectionFactory() {
return new LettuceConnectionFactory();
}
}
```
方案实现
在基于Session的前后端分离中,需要实现如下步骤:
- 用户登录成功后,后端生成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());
}
}
```
- 前端保存JSESSIONID,并在每次请求时携带JSESSIONID。
javascript
const sessionId = localStorage.getItem('sessionId');
axios.defaults.headers.common['Cookie'] = `JSESSIONID=${sessionId}`;
- 后端根据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技术站