SpringSecurity+JWT实现前后端分离的使用详解

创建一个配置类 SecurityConfig 继承 WebSecurityConfigurerAdapter

package top.ryzeyang.demo.common.config;

import org.springframework.context.annotation.Bean;
import org.springframework.security.access.hierarchicalroles.RoleHierarchy;
import org.springframework.security.access.hierarchicalroles.RoleHierarchyImpl;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import top.ryzeyang.demo.common.filter.JwtAuthenticationTokenFilter;
import top.ryzeyang.demo.utils.JwtTokenUtil;

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

  final AuthenticationFailureHandler authenticationFailureHandler;
  final AuthenticationSuccessHandler authenticationSuccessHandler;
  final AuthenticationEntryPoint authenticationEntryPoint;
  final AccessDeniedHandler accessDeniedHandler;
  final LogoutSuccessHandler logoutSuccessHandler;

  public SecurityConfig(AuthenticationFailureHandler authenticationFailureHandler, AuthenticationSuccessHandler authenticationSuccessHandler, AuthenticationEntryPoint authenticationEntryPoint, AccessDeniedHandler accessDeniedHandler, LogoutSuccessHandler logoutSuccessHandler) {
    this.authenticationFailureHandler = authenticationFailureHandler;
    this.authenticationSuccessHandler = authenticationSuccessHandler;
    this.authenticationEntryPoint = authenticationEntryPoint;
    this.accessDeniedHandler = accessDeniedHandler;
    this.logoutSuccessHandler = logoutSuccessHandler;
  }

  @Bean
  public PasswordEncoder passwordEncoder() {
    return PasswordEncoderFactories.createDelegatingPasswordEncoder();
  }

  @Bean("users")
  public UserDetailsService users() {
    UserDetails user = User.builder()
        .username("user")
        .password("{bcrypt}$2a$10$1.NSMxlOyMgJHxOi8CWwxuU83G0/HItXxRoBO4QWZMTDp0tzPbCf.")
        .roles("USER")
//        roles 和 authorities 不能并存
//        .authorities(new String[]{"system:user:query", "system:user:edit", "system:user:delete"})
        .build();
    UserDetails admin = User.builder()
        .username("admin")
        .password("{bcrypt}$2a$10$1.NSMxlOyMgJHxOi8CWwxuU83G0/HItXxRoBO4QWZMTDp0tzPbCf.")
//        .roles("USER", "ADMIN")
        .roles("ADMIN")
//        .authorities("system:user:create")
        .build();
    return new InMemoryUserDetailsManager(user, admin);
  }

  /**
   * 角色继承:
   * 让Admin角色拥有User的角色的权限
   * @return
   */
  @Bean
  public RoleHierarchy roleHierarchy() {
    RoleHierarchyImpl result = new RoleHierarchyImpl();
    result.setHierarchy("ROLE_ADMIN > ROLE_USER");
    return result;
  }

  @Override
  protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(users());
  }

  @Override
  public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers("/js/**", "/css/**", "/images/**");
  }

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    // 自定义异常处理
    http.exceptionHandling()
        .authenticationEntryPoint(authenticationEntryPoint)
        .accessDeniedHandler(accessDeniedHandler)

        // 权限
        .and()
        .authorizeRequests()
        // 一个是必须待身份信息但是不校验权限。
        .antMatchers("/", "/mock/login").permitAll()
        //只允许匿名访问
        .antMatchers("/hello").anonymous()
        .anyRequest()
        .authenticated()

        // 表单登录
//        .and()
//        .formLogin()
//        .successHandler(authenticationSuccessHandler)
//        .failureHandler(authenticationFailureHandler)
//        .loginProcessingUrl("/login")
//        .permitAll()

        // 注销
        .and()
        .logout()
        .logoutUrl("/logout")
        .logoutSuccessHandler(logoutSuccessHandler)
        .permitAll()

        // 关闭csrf 会在页面中生成一个csrf_token
        .and()
        .csrf()
        .disable()

        // 基于token,所以不需要session
        .sessionManagement()
        .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
// 添加我们的JWT过滤器
    .and()
    .addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class)
    ;
  }

  @Bean
  public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter(){
    return new JwtAuthenticationTokenFilter();
  }

  @Bean
  @Override
  public AuthenticationManager authenticationManagerBean() throws Exception {
    return super.authenticationManagerBean();
  }

  @Bean
  public JwtTokenUtil jwtTokenUtil() {
    return new JwtTokenUtil();
  }
}

创建JWT过滤器 继承 OncePerRequestFilter

这里直接用的macro大佬 mall商城里的例子

package top.ryzeyang.demo.common.filter;

import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import top.ryzeyang.demo.utils.JwtTokenUtil;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * JWT登录授权过滤器
 *
 * @author macro
 * @date 2018/4/26
 */
@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
  @Qualifier("users")
  @Autowired
  private UserDetailsService userDetailsService;
  @Autowired
  private JwtTokenUtil jwtTokenUtil;
  @Value("${jwt.tokenHeader}")
  private String tokenHeader;
  @Value("${jwt.tokenHead}")
  private String tokenHead;
  @Override
  protected void doFilterInternal(HttpServletRequest request,
                  HttpServletResponse response,
                  FilterChain chain) throws ServletException, IOException {
    String authHeader = request.getHeader(this.tokenHeader);
    if (authHeader != null && authHeader.startsWith(this.tokenHead)) {
      String authToken = authHeader.substring(this.tokenHead.length());// The part after "Bearer "
      String username = jwtTokenUtil.getUserNameFromToken(authToken);
      log.info("checking username:{}", username);
      if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
        UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
        if (jwtTokenUtil.validateToken(authToken, userDetails)) {
          UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
          authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
          log.info("authenticated user:{}", username);
          SecurityContextHolder.getContext().setAuthentication(authentication);
        }
      }
    }
    chain.doFilter(request, response);
  }
}

自定义handler

这里主要介绍两个hanler,一个权限不足,一个验证失败的

权限不足 实现 AccessDeniedHandler

package top.ryzeyang.demo.common.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.stereotype.Component;
import top.ryzeyang.demo.common.api.CommonResult;
import top.ryzeyang.demo.common.api.ResultEnum;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@Component
public class MyAccessDeineHandler implements AccessDeniedHandler {
  @Override
  public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
    httpServletResponse.setContentType("application/json;charset=utf-8");
    PrintWriter writer = httpServletResponse.getWriter();
    writer.write(new ObjectMapper().writeValueAsString(new CommonResult<>(ResultEnum.ACCESS_ERROR, e.getMessage())));
    writer.flush();
    writer.close();
  }
}

认证失败 实现 AuthenticationEntryPoint

如账号密码错误等验证不通过时

package top.ryzeyang.demo.common.handler;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import top.ryzeyang.demo.common.api.CommonResult;
import top.ryzeyang.demo.common.api.ResultEnum;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@Component
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
  @Override
  public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
    httpServletResponse.setContentType("application/json;charset=utf-8");
    PrintWriter writer = httpServletResponse.getWriter();
//     认证失败
    writer.write(new ObjectMapper().writeValueAsString(new CommonResult<>(ResultEnum.AUTHENTICATION_ERROR, e.getMessage())));
    writer.flush();
    writer.close();
  }
}

JWT工具类

这里直接用的macro大佬 mall商城里的例子

稍微改了一点,因为 JDK11 用的 jjwt 版本不一样 ,语法也有些不同

pom 文件中引入 jjwt

<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt-api</artifactId>
  <version>0.11.2</version>
</dependency>
<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt-impl</artifactId>
  <version>0.11.2</version>
  <scope>runtime</scope>
</dependency>
<dependency>
  <groupId>io.jsonwebtoken</groupId>
  <artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred -->
  <version>0.11.2</version>
  <scope>runtime</scope>
</dependency>

JwtTokenUtil

package top.ryzeyang.demo.utils;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.io.Decoders;
import io.jsonwebtoken.security.Keys;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;

import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * JwtToken生成的工具类
 * JWT token的格式:header.payload.signature
 * header的格式(算法、token的类型):
 * {"alg": "HS512","typ": "JWT"}
 * payload的格式(用户名、创建时间、生成时间):
 * {"sub":"wang","created":1489079981393,"exp":1489684781}
 * signature的生成算法:
 * HMACSHA512(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
 *
 * @author macro
 * @date 2018/4/26
 */
@Slf4j
public class JwtTokenUtil {
  private static final String CLAIM_KEY_USERNAME = "sub";
  private static final String CLAIM_KEY_CREATED = "created";

  @Value("${jwt.secret}")
  private String secret;

  @Value("${jwt.expiration}")
  private Long expiration;

  @Value("${jwt.tokenHead}")
  private String tokenHead;

  private SecretKey getSecretKey() {
    byte[] encodeKey = Decoders.BASE64.decode(secret);
    return Keys.hmacShaKeyFor(encodeKey);
  }

  /**
   * 根据负责生成JWT的token
   */
  private String generateToken(Map<String, Object> claims) {
    SecretKey secretKey = getSecretKey();
    return Jwts.builder()
        .setClaims(claims)
        .setExpiration(generateExpirationDate())
        .signWith(secretKey)
        .compact();
  }

  /**
   * 测试生成的token
   * @param claims
   * @return
   */
  public String generateToken2(Map<String, Object> claims) {
    SecretKey secretKey = getSecretKey();
    return Jwts.builder()
        .setClaims(claims)
        .setIssuer("Java4ye")
        .setExpiration(new Date(System.currentTimeMillis() + 1 * 1000))
        .signWith(secretKey)
        .compact();
  }

  /**
   * 从token中获取JWT中的负载
   */
  private Claims getClaimsFromToken(String token) {
    SecretKey secretKey = getSecretKey();
    Claims claims = null;
    try {
      claims = Jwts.parserBuilder()
          .setSigningKey(secretKey)
          .build()
          .parseClaimsJws(token)
          .getBody();
    } catch (Exception e) {
      log.info("JWT格式验证失败:{}", token);
    }
    return claims;
  }

  /**
   * 生成token的过期时间
   */
  private Date generateExpirationDate() {
    return new Date(System.currentTimeMillis() + expiration * 1000);
  }

  /**
   * 从token中获取登录用户名
   */
  public String getUserNameFromToken(String token) {
    String username;
    try {
      Claims claims = getClaimsFromToken(token);
      username = claims.getSubject();
    } catch (Exception e) {
      username = null;
    }
    return username;
  }

  /**
   * 验证token是否还有效
   *
   * @param token    客户端传入的token
   * @param userDetails 从数据库中查询出来的用户信息
   */
  public boolean validateToken(String token, UserDetails userDetails) {
    String username = getUserNameFromToken(token);
    return username.equals(userDetails.getUsername()) && !isTokenExpired(token);
  }

  /**
   * 判断token是否已经失效
   */
  private boolean isTokenExpired(String token) {
    Date expiredDate = getExpiredDateFromToken(token);
    return expiredDate.before(new Date());
  }

  /**
   * 从token中获取过期时间
   */
  private Date getExpiredDateFromToken(String token) {
    Claims claims = getClaimsFromToken(token);
    return claims.getExpiration();
  }

  /**
   * 根据用户信息生成token
   */
  public String generateToken(UserDetails userDetails) {
    Map<String, Object> claims = new HashMap<>();
    claims.put(CLAIM_KEY_USERNAME, userDetails.getUsername());
    claims.put(CLAIM_KEY_CREATED, new Date());
    return generateToken(claims);
  }

  /**
   * 当原来的token没过期时是可以刷新的
   *
   * @param oldToken 带tokenHead的token
   */
  public String refreshHeadToken(String oldToken) {
    if (StrUtil.isEmpty(oldToken)) {
      return null;
    }
    String token = oldToken.substring(tokenHead.length());
    if (StrUtil.isEmpty(token)) {
      return null;
    }
    //token校验不通过
    Claims claims = getClaimsFromToken(token);
    if (claims == null) {
      return null;
    }
    //如果token已经过期,不支持刷新
    if (isTokenExpired(token)) {
      return null;
    }
    //如果token在30分钟之内刚刷新过,返回原token
    if (tokenRefreshJustBefore(token, 30 * 60)) {
      return token;
    } else {
      claims.put(CLAIM_KEY_CREATED, new Date());
      return generateToken(claims);
    }
  }

  /**
   * 判断token在指定时间内是否刚刚刷新过
   *
   * @param token 原token
   * @param time 指定时间(秒)
   */
  private boolean tokenRefreshJustBefore(String token, int time) {
    Claims claims = getClaimsFromToken(token);
    Date created = claims.get(CLAIM_KEY_CREATED, Date.class);
    Date refreshDate = new Date();
    //刷新时间在创建时间的指定时间内
    if (refreshDate.after(created) && refreshDate.before(DateUtil.offsetSecond(created, time))) {
      return true;
    }
    return false;
  }
}

配置文件 application.yml

这里的 secret 可以用该方法生成

@Test
  void generateKey() {
    /**
     * SECRET 是签名密钥,只生成一次即可,生成方法:
     * Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
     * String secretString = Encoders.BASE64.encode(key.getEncoded()); # 本文使用 BASE64 编码
     * */
    Key key = Keys.secretKeyFor(SignatureAlgorithm.HS512);
    String secretString = Encoders.BASE64.encode(key.getEncoded());
    System.out.println(secretString);
//    Blk1X8JlN4XH4s+Kuc0YUFXv+feyTgVUMycSiKbiL0YhRddy872mCNZBGZIb57Jn2V1RtaFXIxs8TvNPsnG//g==
  }
jwt:
 tokenHeader: Authorization
 secret: Blk1X8JlN4XH4s+Kuc0YUFXv+feyTgVUMycSiKbiL0YhRddy872mCNZBGZIb57Jn2V1RtaFXIxs8TvNPsnG//g==
 expiration: 604800
 tokenHead: 'Bearer '

server:
 servlet:
 context-path: /api

Controller

AuthController

package top.ryzeyang.demo.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.bind.annotation.*;
import top.ryzeyang.demo.common.api.CommonResult;
import top.ryzeyang.demo.model.dto.UserDTO;
import top.ryzeyang.demo.utils.CommonResultUtil;
import top.ryzeyang.demo.utils.JwtTokenUtil;

import java.util.Collection;

@RestController
@ResponseBody
@RequestMapping("/mock")
public class AuthController {
  @Autowired
  private JwtTokenUtil jwtTokenUtil;
  @Qualifier("users")
  @Autowired
  private UserDetailsService userDetailsService;
  @Autowired
  AuthenticationManager authenticationManager;

  @GetMapping("/userinfo")
  public CommonResult<Collection<? extends GrantedAuthority>> getUserInfo(){
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
    return CommonResultUtil.success(authorities);
  }

  /**
   * 模拟登陆
   */
  @PostMapping("/login")
  public CommonResult<String> login(@RequestBody UserDTO userDTO){
    String username = userDTO.getUsername();
    String password = userDTO.getPassword();
    UsernamePasswordAuthenticationToken token
        = new UsernamePasswordAuthenticationToken(username, password);
    Authentication authenticate = authenticationManager.authenticate(token);
    SecurityContextHolder.getContext().setAuthentication(authenticate);
    UserDetails userDetails = userDetailsService.loadUserByUsername(username);
    String t = jwtTokenUtil.generateToken(userDetails);
    return CommonResultUtil.success(t);
  }
}

HelloController

package top.ryzeyang.demo.controller;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

  @GetMapping("/hello")
  public String hello() {
    return "hello";
  }

  @GetMapping("/hello/anonymous")
  public String hello2() {
    return "anonymous";
  }

  @PreAuthorize("hasRole('ADMIN')")
  @GetMapping("/hello/admin")
  public String helloAdmin() {
    return "hello Admin";
  }

  @PreAuthorize("hasRole('USER')")
  @GetMapping("/hello/user")
  public String helloUser() {
    return "hello user";
  }

  @PreAuthorize("hasAnyAuthority('system:user:query')")
  @GetMapping("/hello/user2")
  public String helloUser2() {
    return "hello user2";
  }
}

项目地址在GitHub上

地址:SpringSecurity-Vuetify-Permissions-demo

到此这篇关于SpringSecurity+JWT实现前后端分离的使用详解的文章就介绍到这了,更多相关SpringSecurity+JWT前后端分离内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringBoot+Vue前后端分离,使用SpringSecurity完美处理权限问题的解决方法

    当前后端分离时,权限问题的处理也和我们传统的处理方式有一点差异.笔者前几天刚好在负责一个项目的权限管理模块,现在权限管理模块已经做完了,我想通过5-6篇文章,来介绍一下项目中遇到的问题以及我的解决方案,希望这个系列能够给小伙伴一些帮助.本系列文章并不是手把手的教程,主要介绍了核心思路并讲解了核心代码,完整的代码小伙伴们可以在GitHub上star并clone下来研究.另外,原本计划把项目跑起来放到网上供小伙伴们查看,但是之前买服务器为了省钱,内存只有512M,两个应用跑不起来(已经有一个V部落开

  • SpringSecurity+JWT实现前后端分离的使用详解

    创建一个配置类 SecurityConfig 继承 WebSecurityConfigurerAdapter package top.ryzeyang.demo.common.config; import org.springframework.context.annotation.Bean; import org.springframework.security.access.hierarchicalroles.RoleHierarchy; import org.springframework

  • CI框架实现框架前后端分离的方法详解

    本文实例分析了CI框架实现框架前后端分离的方法.分享给大家供大家参考,具体如下: 我们在使用CI框架开发中(或者使用所有的其他框架开发中),总是习惯于将所开发项目的前端和后台分离开来,将各自的控制器.模型或视图放到不同的文件夹中.在这一点上ThinkPHP框架要比CI框架更要方便一些.这篇博文我们就讲述如何实现根据CI框架开发将前后端实现分离.(本文以本人开发的Pkadmin后台管理系统为例,Pkadmin后台管理系统是基于Codeigniter_v3.1.0框架和Olive Admin后台模版

  • Spring boot整合shiro+jwt实现前后端分离

    本文实例为大家分享了Spring boot整合shiro+jwt实现前后端分离的具体代码,供大家参考,具体内容如下 这里内容很少很多都为贴的代码,具体内容我经过了看源码和帖子加了注释.帖子就没用太多的内容 先下载shiro和jwt的jar包 <!-- shiro包 --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId

  • SpringBoot+Vue+JWT的前后端分离登录认证详细步骤

    前后端分离的概念在现在很火,最近也学习了一下前后端分离的登录认证. 创建后端springboot工程 这个很简单了,按照idea的一步一步创建就行 文件目录结构: pom文件依赖导入. <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </

  • 详解SpringSecurity如何实现前后端分离

    目录 Spring Security存在的问题 改造Spring Security的认证方式 1. 登录请求改成JSON方式 1.1 新建JSON版Filter - JsonUsernamePasswordAuthenticationFilter 1.2 新建Configurer来注册Filter - JsonUsernamePasswordLoginConfigurer 1.3 将自定义Configurer注册到HttpSecurity上 2. 关闭页面重定向 2.1 当前用户未登录 2.2

  • nginx+vue.js实现前后端分离的示例代码

    1.nginx 是一个高性能的HTTP和反向代理服务器,常用于分布式服务器管理. 它常用于做负载均衡(通过调用多台服务器达到此目的) 静态资源输出更快,可以对资源实现gzip压缩后输出(这也是本文为什么用它做静态资源访问的一个重要原因) 适合解决跨域问题和反向代理(因为谁也不想看到在本域名下看到访问其他域名的情况发生,跨域可导致csrf攻击,这是本文用它的第二个原因) 占用内存少,秒启,能快速切换结点,防止宕机 2.es6 是ECMAScript的第六个版本,如果想要学好vue.js等js框架,

  • Spring Boot + Vue 前后端分离项目如何踢掉已登录用户

    上篇文章中,我们讲了在 Spring Security 中如何踢掉前一个登录用户,或者禁止用户二次登录,通过一个简单的案例,实现了我们想要的效果. 但是有一个不太完美的地方,就是我们的用户是配置在内存中的用户,我们没有将用户放到数据库中去.正常情况下,松哥在 Spring Security 系列中讲的其他配置,大家只需要参考Spring Security+Spring Data Jpa 强强联手,安全管理只有更简单!一文,将数据切换为数据库中的数据即可. 本文是本系列的第十三篇,阅读前面文章有助

  • springboot解决前后端分离时的跨域问题

    随着分布式微服务的兴起,越来越多的公司在开发web项目的时候选择前后端分离的模式开发,前后端分开部署,使得分工更加明确,彻底解放了前端. 我们知道,http请求都是无状态,现在比较流行的都是jwt的形式处理无状态的请求,在请求头上带上认证参数(token等),前后端分离有好处,也有坏处,第一次开发前后端分离项目的人,肯定会遇到前端请求跨域的问题,这个怎么处理呢?在说处理方案前,有必要说明一下为什么会跨域和什么是跨域? 一.为什么会跨域? 出于浏览器的同源策略限制.同源策略(Sameoriginp

  • SpringBoot+MyBatisPlus+Vue 前后端分离项目快速搭建过程(后端)

    数据库准备 data_test.sql: /* SQLyog Enterprise v12.08 (64 bit) MySQL - 5.7.31 : Database - data_test ********************************************************************* */ /*!40101 SET NAMES utf8 */; /*!40101 SET SQL_MODE=''*/; /*!40014 SET @OLD_UNIQUE_

随机推荐