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

yizhihongxing

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

  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日

相关文章

  • XML简介

    XML简介 XML(可扩展标记语言)是一种用于描述文档内容的标记语言,它使用标签来标识文档中各个部分的含义,并通过这些标记实现对文档内容的组织、表示和传输。相较于 HTML 等文档语言,XML 更加通用灵活,可以应用于各种场景。 XML 基础结构 XML 文档由各种元素构成,每个元素包含一个标记和一个值(也称为“内容”或“文本”)。标记用来表示该元素的类型和…

    Java 2023年5月26日
    00
  • js如何设置在iframe框架中指定div不显示

    使用JavaScript直接在iframe中指定div不显示的方法: 在iframe框架中使用JavaScript来控制指定div元素的display属性,让其不显示。可以使用以下的代码实现: <!DOCTYPE html> <html> <head> <meta charset="UTF-8"&…

    Java 2023年6月16日
    00
  • Hibernate框架中的缓存技术详解

    Hibernate框架中的缓存技术详解 什么是缓存? 缓存是一种提高数据库读写效率的技术。在Hibernate中,会将经常访问的数据缓存到内存中,可在内存中对该数据进行读写操作,从而提高查询效率,减少I/O操作的次数,保证了数据查询的高效性。 Hibernate中的缓存分类 Hibernate的缓存主要分为二级缓存和查询缓存: 二级缓存 二级缓存是在Sess…

    Java 2023年5月20日
    00
  • jsp实现仿QQ空间新建多个相册名称并向相册中添加照片功能

    实现仿QQ空间新建多个相册名称并向相册中添加照片功能需要进行以下步骤: 准备工作 确定基础环境:使用JSP,需要安装Java和Tomcat等环境。 安装数据库:本文以MySQL为例进行讲解,需要安装MySQL数据库,并创建相应的数据库和表格。 创建数据库和表格 在MySQL中创建相应的数据库,例如“photo_album”。 在该数据库下创建两个表格:一个用…

    Java 2023年6月15日
    00
  • Spring通过Java配置集成Tomcat的方法

    下面我来详细讲解“Spring通过Java配置集成Tomcat的方法”的完整攻略,首先需要明确以下几个步骤: 导入相关依赖库; 编写Spring配置文件; 编写Java配置类; 启动Tomcat服务器。 下面我会逐一讲解每一个步骤,并提供两个示例供参考。 1. 导入相关依赖库 在项目的pom.xml或build.gradle文件中加入以下依赖库: <!…

    Java 2023年5月19日
    00
  • Java获取当地的日出日落时间代码分享

    来讲解如何获取当地的日出日落时间。需要的工具是Java的日期时间api和一个名为SunsetSunrise的开源库。 导入SunsetSunrise库: 首先需要在Java项目中导入SunsetSunrise库。该库可以在Github上进行下载: https://github.com/davidmoten/sunsetsunrise 可以选择下载源代码然后进…

    Java 2023年5月20日
    00
  • Java 如何读取Excel格式xls、xlsx数据工具类

    Java如何读取Excel格式xls、xlsx数据 在Java中,我们可以使用POI库来操作Excel文件,这个库支持读取和写入Excel文件。下面我们将通过两个示例来讲解如何读取Excel格式xls、xlsx数据。 示例1:读取Excel文件中的数据 首先我们需要引入相关依赖。在pom.xml文件中添加以下配置: <dependencies> …

    Java 2023年5月19日
    00
  • java垃圾回收原理之GC算法基础

    Java垃圾回收原理之GC算法基础 垃圾回收是Java的一项基本功能,它帮助程序员释放不再使用的内存。Java中的垃圾回收器使用了多种垃圾回收算法。GC算法的选择和调优对程序的性能有很大的影响。为了更好的理解Java中的GC算法,我们需要首先掌握垃圾的判定方法和垃圾回收算法的分类。 垃圾回收 Java 中的垃圾回收主要是通过判断对象是否还有引用指向来进行判定…

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