SpringSecurity实现前后端分离的示例详解

为了讲解本文的主题,我们需要先了解以下几个概念:

  1. 前后端分离:前后端分离是指将前端和后端业务逻辑分开,前端主要负责展示数据和交互逻辑,后端主要负责提供API接口和业务逻辑。
  2. Spring Security:Spring Security是基于Spring框架的安全框架,主要提供身份认证、授权、攻击防护等安全功能。
  3. Token认证:Token认证是一种基于Token的认证方式,服务器在认证成功后返回给客户端一个Token,客户端在请求接口时需要将Token带上,服务器根据Token来判断客户端的身份。

接下来,我们将讲解如何使用Spring Security实现前后端分离的示例。

一、前端实现Token认证

前端实现Token认证的目的是为了在用户成功登录后,获取Token并将其保存在本地,然后在访问需要认证的API接口时将Token带上。这里我们使用Vue.js作为前端框架,使用Axios作为http库,示例代码如下:

import axios from 'axios';

// 设置Token
function setToken(token) {
  localStorage.setItem('Token', token);
}

// 获取Token
function getToken() {
  return localStorage.getItem('Token');
}

// 在请求头中添加Token
axios.interceptors.request.use(function(config) {
  const token = getToken();
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

// 登录
export function login(username, password) {
  return axios.post('/api/login', {
    username,
    password,
  }).then(response => {
    const token = response.data.token;
    setToken(token);
    return response;
  });
}

如上代码中,我们使用localStorage保存Token,在请求头中添加Token。当用户成功登录后,我们调用login方法,将用户名和密码传给后端验证,验证成功后将Token保存在本地,并返回登录成功的响应结果。

二、后端实现Token认证

后端实现Token认证的主要目的是为了在客户端请求接口时,根据Token判断客户端的身份是否合法,如果合法则返回数据,如果不合法则返回401状态码。这里我们使用Spring Security提供的Token认证方式。

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

  @Autowired
  private UserDetailsService userDetailsService;

  @Autowired
  private SecurityProperties securityProperties;

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    // 开启跨域
    http.cors().and().csrf().disable();

    // 登录过滤器
    http.addFilterBefore(new JwtAuthenticationFilter(securityProperties), UsernamePasswordAuthenticationFilter.class);

    // 其他请求都需要认证
    http.authorizeRequests().anyRequest().authenticated();
  }

  @Autowired
  public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
  }

  @Bean
  public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
  }

}

如上代码中,我们在SecurityConfig类中使用@EnableWebSecurity注解开启Spring Security,并将UserDetailsService注入到configureGlobal方法中,以便为用户提供身份认证功能。另外,我们还通过HttpSecurity对象开启跨域、添加Token认证过滤器并设置其他的请求都需要认证。

具体的Token认证过滤器JwtAuthenticationFilter实现如下:

public class JwtAuthenticationFilter extends OncePerRequestFilter {

  private final SecurityProperties securityProperties;

  public JwtAuthenticationFilter(SecurityProperties securityProperties) {
    this.securityProperties = securityProperties;
  }

  @Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
    String token = getTokenFromRequest(request);

    if (StringUtils.hasText(token) && JwtUtils.validateToken(token, securityProperties.getSecret())) {
      Authentication authentication = JwtUtils.getAuthentication(token);
      SecurityContextHolder.getContext().setAuthentication(authentication);
    } else {
      SecurityContextHolder.getContext().setAuthentication(null);
    }

    chain.doFilter(request, response);
  }

  private String getTokenFromRequest(HttpServletRequest request) {
    String bearerToken = request.getHeader("Authorization");
    if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
      return bearerToken.substring(7);
    }
    return null;
  }

}

如上代码中,我们从request中获取Token并校验,如果校验通过,则将认证信息存入SecurityContextHolder中,如登录名、角色等信息。

三、示例一:请求用户信息

假设我们的系统中,需要获取当前登录用户的信息,我们可以在前端使用Axios在请求时将Token带上,如下:

function getUserInfo() {
  return axios.get('/api/user/info').then(response => {
    const userInfo = response.data.userInfo;
    return userInfo;
  });
}

如上代码中,我们调用getUserInfo方法,Axios会自动将Token带上,并向后端请求用户信息。

然后,在后端我们需要编写一个接口获取当前登录用户的信息,示例代码如下:

@RestController
@RequestMapping("/api/user")
public class UserController {

  @GetMapping("/info")
  @PreAuthorize("hasRole('ADMIN')")
  public Map<String, Object> getUserInfo(Authentication authentication) {
    Map<String, Object> user = new HashMap<>();
    if (authentication != null) {
      user.put("username", authentication.getName());
      user.put("roles", authentication.getAuthorities());
    }
    return Collections.singletonMap("userInfo", user);
  }

}

如上代码中,我们使用@PreAuthorize("hasRole('ADMIN')")注解来设置只有ADMIN角色的用户才可以访问该接口。当请求进来后,Spring Security会根据Token认证客户端的身份,并判断客户端是否具有ADMIN角色,最终根据判断结果返回用户信息或401状态码。

四、示例二:使用Token登录

接下来我们将讲解使用Token登录的示例。用户在登录时输入用户名和密码,后端根据用户名和密码验证成功返回Token,前端将Token保存在本地,在请求API时带上Token。我们在前端添加如下方法用于登录:

export function login(username, password) {
  return axios.post('/api/login', {
    username,
    password,
  }).then(response => {
    const token = response.data.token;
    setToken(token);
    return response;
  });
}

如上代码中,我们使用Axios向后端发送登录请求,如果成功则将返回的Token保存到本地。

接下来,我们需要在后端添加登录接口,示例代码如下:

@RestController
public class LoginController {

  @Autowired
  private AuthenticationManager authenticationManager;

  @Autowired
  private JwtProperties jwtProperties;

  @PostMapping("/api/login")
  public Map<String, Object> login(@RequestBody LoginRequest request) {
    final String username = request.getUsername();
    final String password = request.getPassword();

    Authentication authentication = null;
    try {
      authentication = authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
    } catch (BadCredentialsException e) {
      throw new RuntimeException("用户名或密码错误");
    }

    String token = generateJwtToken(authentication);

    return Collections.singletonMap("token", token);
  }

  private String generateJwtToken(Authentication authentication) {
    UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
    return JwtUtils.generateToken(userDetails.getUsername(), jwtProperties.getSecret(), jwtProperties.getExpiration());
  }

}

如上代码中,我们使用@PostMapping("/api/login")注解来设置登录接口,当接收到前端的用户名和密码时,使用AuthenticationManager.authenticate方法对用户名和密码进行验证,如果验证通过则生成一个Token,并将Token返回给前端。

至此,我们已经完成了使用Token实现登录的示例。

五、总结

本文详细讲解了使用Spring Security实现前后端分离的示例,涵盖了前端实现Token认证、后端实现Token认证以及两个示例的完整代码。在实际开发中,我们可以根据这个示例进行扩展,实现更加复杂的业务逻辑。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:SpringSecurity实现前后端分离的示例详解 - Python技术站

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

相关文章

  • 详解Java的JDBC中Statement与PreparedStatement对象

    详解Java的JDBC中Statement与PreparedStatement对象 对于访问关系型数据库的Java应用程序来说,JDBC是必不可少的一部分。其中的Statement和PreparedStatement对象则是开发者必须熟练掌握的基本知识点。本篇文章将详细介绍Statement和PreparedStatement对象的概念以及如何在Java应用…

    Java 2023年6月16日
    00
  • SpringBoot使用token简单鉴权的具体实现方法

    一、Token简单鉴权的原理 Token鉴权是一种前后端分离的权限验证方式,具体的原理如下: 用户登录时请求后端API,后端验证用户名和密码是否正确,如果正确,将返回一个Token给前端。 前端将Token保存在本地(通常是localStorage或sessionStorage),后续请求时需要将Token附带在请求头中发送给后端。 后端验证请求头中的Tok…

    Java 2023年5月20日
    00
  • 什么是EVB?EVB技术的简单介绍

    下面是关于EVB的详细讲解。 什么是EVB? EVB全称为Evaluation Board(评估板),是一种硬件开发工具,用于快速评估和开发不同种类的芯片、模块、传感器等硬件设备。它通常包括主板、外设接口、调试器等硬件和相关的软件开发工具。EVB与PCB(Printed Circuit Board,印刷电路板)相比,更注重快速原型和快速评估,能够快速搭建出一…

    Java 2023年6月15日
    00
  • Java ArrayList源码深入分析

    Java ArrayList源码深入分析 概述 Java中的ArrayList是最基础的动态数组实现,是Java集合框架中的重要组成部分。本文将分析ArrayList源码,通过详细的代码解析和实例说明,深入分析ArrayList的内部实现原理。 前置知识 在深入分析ArrayList源码之前,需要具备以下基础知识: Java集合框架的基本概念和应用场景 数组…

    Java 2023年5月26日
    00
  • 一文带你认识java中的String类

    String类在Java中是一个非常重要的类,它用来表示字符串,下面就一文带你认识Java中的String类。 1. String类的概述 在Java中,字符串是一个非常常见的数据类型。而String类则是Java提供的处理字符串的主要类。String类是不可变的,也就是说一旦创建了一个String对象,便不能再进行修改。每进行一次字符串的操作,都会创建一个…

    Java 2023年5月26日
    00
  • Java 如何同时返回多个不同类型

    实现 Java 同时返回多个不同类型的方法可以有多种,以下是三种可行的方案: 方案一:利用类封装多个返回值 在 Java 中,可以使用一个类封装多个返回值。通过定义一个类(比如下面的 Result 类),该类包含多个字段,每个字段表示一个要返回的值,然后在需要返回多个值的函数中,可以将这些值封装并返回一个 Result 类的实例。以下是实现过程的示例: pu…

    Java 2023年5月26日
    00
  • Springboot+SpringSecurity+JWT实现用户登录和权限认证示例

    让我为您详细讲解一下“Springboot+SpringSecurity+JWT实现用户登录和权限认证示例”的攻略。 首先,需要安装以下工具: Java开发环境 Maven构建工具 然后,我们需要按照以下步骤进行实现: 1.添加依赖 在pom.xml文件中添加以下依赖: <dependency> <groupId>org.spring…

    Java 2023年5月20日
    00
  • 常用字符集编码详解(ASCII GB2312 GBK GB18030 unicode UTF-8)

    常用字符集编码详解 在计算机中,各种语言的字符需要通过字符编码来表示,常见的字符集编码包括ASCII、GB2312、GBK、GB18030、unicode、UTF-8。下面逐一介绍。 ASCII ASCII(American Standard Code for Information Interchange)是最早的字符编码,将每个字符用7位十进制数表示。编…

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