Spring Security结合JWT的方法教程

概述

众所周知使用 JWT 做权限验证,相比 Session 的优点是,Session 需要占用大量服务器内存,并且在多服务器时就会涉及到共享 Session 问题,在手机等移动端访问时比较麻烦

而 JWT 无需存储在服务器,不占用服务器资源(也就是无状态的),用户在登录后拿到 Token 后,访问需要权限的请求时附上 Token(一般设置在Http请求头),JWT 不存在多服务器共享的问题,也没有手机移动端访问问题,若为了提高安全,可将 Token 与用户的 IP 地址绑定起来

前端流程

用户通过 AJAX 进行登录得到一个 Token

之后访问需要权限请求时附上 Token 进行访问

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>Title</title>
 <script src="http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
 <script type="application/javascript">
  var header = "";
  function login() {
   $.post("http://localhost:8080/auth/login", {
    username: $("#username").val(),
    password: $("#password").val()
   }, function (data) {
    console.log(data);
    header = data;
   })
  }
  function toUserPageBtn() {
   $.ajax({
    type: "get",
    url: "http://localhost:8080/userpage",
    beforeSend: function (request) {
     request.setRequestHeader("Authorization", header);
    },
    success: function (data) {
     console.log(data);
    }
   });
  }
 </script>
</head>
<body>
 <fieldset>
  <legend>Please Login</legend>
  <label>UserName</label><input type="text" id="username">
  <label>Password</label><input type="text" id="password">
  <input type="button" onclick="login()" value="Login">
 </fieldset>
 <button id="toUserPageBtn" onclick="toUserPageBtn()">访问UserPage</button>
</body>
</html>

后端流程(Spring Boot + Spring Security + JJWT)

思路:

  • 创建用户、权限实体类与数据传输对象
  • 编写 Dao 层接口,用于获取用户信息
  • 实现 UserDetails(Security 支持的用户实体对象,包含权限信息)
  • 实现 UserDetailsSevice(从数据库中获取用户信息,并包装成UserDetails)
  • 编写 JWTToken 生成工具,用于生成、验证、解析 Token
  • 配置 Security,配置请求处理 与 设置 UserDetails 获取方式为自定义的 UserDetailsSevice
  • 编写 LoginController,接收用户登录名密码并进行验证,若验证成功返回 Token 给用户
  • 编写过滤器,若用户请求头或参数中包含 Token 则解析,并生成 Authentication,绑定到 SecurityContext ,供 Security 使用
  • 用户访问了需要权限的页面,却没附上正确的 Token,在过滤器处理时则没有生成 Authentication,也就不存在访问权限,则无法访问,否之访问成功

编写用户实体类,并插入一条数据

User(用户)实体类

@Data
@Entity
public class User {
 @Id
 @GeneratedValue
 private int id;
 private String name;
 private String password;
 @ManyToMany(cascade = {CascadeType.REFRESH}, fetch = FetchType.EAGER)
 @JoinTable(name = "user_role", joinColumns = {@JoinColumn(name = "uid", referencedColumnName = "id")}, inverseJoinColumns = {@JoinColumn(name = "rid", referencedColumnName = "id")})
 private List<Role> roles;
} 

Role(权限)实体类

@Data
@Entity
public class Role {
 @Id
 @GeneratedValue
 private int id;
 private String name;
 @ManyToMany(mappedBy = "roles")
 private List<User> users;
}

插入数据

User 表

id name password
1 linyuan 123

Role 表

id name
1 USER

User_ROLE 表

uid rid
1 1

Dao 层接口,通过用户名获取数据,返回值为 Java8 的 Optional 对象

public interface UserRepository extends Repository<User,Integer> {
 Optional<User> findByName(String name);
}

编写 LoginDTO,用于与前端之间数据传输

@Data
public class LoginDTO implements Serializable {
 @NotBlank(message = "用户名不能为空")
 private String username;
 @NotBlank(message = "密码不能为空")
 private String password;
}

编写 Token 生成工具,利用 JJWT 库创建,一共三个方法:生成 Token(返回String)、解析 Token(返回Authentication认证对象)、验证 Token(返回布尔值)

@Component
public class JWTTokenUtils {
 private final Logger log = LoggerFactory.getLogger(JWTTokenUtils.class);
 private static final String AUTHORITIES_KEY = "auth";
 private String secretKey;   //签名密钥
 private long tokenValidityInMilliseconds;  //失效日期
 private long tokenValidityInMillisecondsForRememberMe;  //(记住我)失效日期
 @PostConstruct
 public void init() {
  this.secretKey = "Linyuanmima";
  int secondIn1day = 1000 * 60 * 60 * 24;
  this.tokenValidityInMilliseconds = secondIn1day * 2L;  this.tokenValidityInMillisecondsForRememberMe = secondIn1day * 7L;
 }
 private final static long EXPIRATIONTIME = 432_000_000;
 //创建Token
 public String createToken(Authentication authentication, Boolean rememberMe){
  String authorities = authentication.getAuthorities().stream()  //获取用户的权限字符串,如 USER,ADMIN
    .map(GrantedAuthority::getAuthority)
    .collect(Collectors.joining(","));
  long now = (new Date()).getTime();    //获取当前时间戳
  Date validity;           //存放过期时间
  if (rememberMe){
   validity = new Date(now + this.tokenValidityInMilliseconds);
  }else {
   validity = new Date(now + this.tokenValidityInMillisecondsForRememberMe);
  }
  return Jwts.builder()         //创建Token令牌
    .setSubject(authentication.getName())   //设置面向用户
    .claim(AUTHORITIES_KEY,authorities)    //添加权限属性
    .setExpiration(validity)      //设置失效时间
    .signWith(SignatureAlgorithm.HS512,secretKey) //生成签名
    .compact();
 }
 //获取用户权限
 public Authentication getAuthentication(String token){
  System.out.println("token:"+token);
  Claims claims = Jwts.parser()       //解析Token的payload
    .setSigningKey(secretKey)
    .parseClaimsJws(token)
    .getBody();
  Collection<? extends GrantedAuthority> authorities =
    Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(","))   //获取用户权限字符串
    .map(SimpleGrantedAuthority::new)
    .collect(Collectors.toList());             //将元素转换为GrantedAuthority接口集合
  User principal = new User(claims.getSubject(), "", authorities);
  return new UsernamePasswordAuthenticationToken(principal, "", authorities);
 }
 //验证Token是否正确
 public boolean validateToken(String token){
  try {
   Jwts.parser().setSigningKey(secretKey).parseClaimsJws(token); //通过密钥验证Token
   return true;
  }catch (SignatureException e) {          //签名异常
   log.info("Invalid JWT signature.");
   log.trace("Invalid JWT signature trace: {}", e);
  } catch (MalformedJwtException e) {         //JWT格式错误
   log.info("Invalid JWT token.");
   log.trace("Invalid JWT token trace: {}", e);
  } catch (ExpiredJwtException e) {         //JWT过期
   log.info("Expired JWT token.");
   log.trace("Expired JWT token trace: {}", e);
  } catch (UnsupportedJwtException e) {        //不支持该JWT
   log.info("Unsupported JWT token.");
   log.trace("Unsupported JWT token trace: {}", e);
  } catch (IllegalArgumentException e) {        //参数错误异常
   log.info("JWT token compact of handler are invalid.");
   log.trace("JWT token compact of handler are invalid trace: {}", e);
  }
  return false;
 }
}

实现 UserDetails 接口,代表用户实体类,在我们的 User 对象上在进行包装,包含了权限等性质,可以供 Spring Security 使用

public class MyUserDetails implements UserDetails{
 private User user;
 public MyUserDetails(User user) {
  this.user = user;
 }
 @Override
 public Collection<? extends GrantedAuthority> getAuthorities() {
  List<Role> roles = user.getRoles();
  List<GrantedAuthority> authorities = new ArrayList<>();
  StringBuilder sb = new StringBuilder();
  if (roles.size()>=1){
   for (Role role : roles){
    authorities.add(new SimpleGrantedAuthority(role.getName()));
   }
   return authorities;
  }
  return AuthorityUtils.commaSeparatedStringToAuthorityList("");
 }
 @Override
 public String getPassword() {
  return user.getPassword();
 }
 @Override
 public String getUsername() {
  return user.getName();
 }
 @Override
 public boolean isAccountNonExpired() {
  return true;
 }
 @Override
 public boolean isAccountNonLocked() {
  return true;
 }
 @Override
 public boolean isCredentialsNonExpired() {
  return true;
 }
 @Override
 public boolean isEnabled() {
  return true;
 }
}

实现 UserDetailsService 接口,该接口仅有一个方法,用来获取 UserDetails,我们可以从数据库中获取 User 对象,然后将其包装成 UserDetails 并返回

@Service
public class MyUserDetailsService implements UserDetailsService {
 @Autowired
 UserRepository userRepository;
 @Override
 public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
  //从数据库中加载用户对象
  Optional<User> user = userRepository.findByName(s);
  //调试用,如果值存在则输出下用户名与密码
  user.ifPresent((value)->System.out.println("用户名:"+value.getName()+" 用户密码:"+value.getPassword()));
  //若值不再则返回null
  return new MyUserDetails(user.orElse(null));
 }
}

编写过滤器,用户如果携带 Token 则获取 Token,并根据 Token 生成 Authentication 认证对象,并存放到 SecurityContext 中,供 Spring Security 进行权限控制

public class JwtAuthenticationTokenFilter extends GenericFilterBean {
 private final Logger log = LoggerFactory.getLogger(JwtAuthenticationTokenFilter.class);
 @Autowired
 private JWTTokenUtils tokenProvider;
 @Override
 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
  System.out.println("JwtAuthenticationTokenFilter");
  try {
   HttpServletRequest httpReq = (HttpServletRequest) servletRequest;
   String jwt = resolveToken(httpReq);
   if (StringUtils.hasText(jwt) && this.tokenProvider.validateToken(jwt)) {   //验证JWT是否正确
    Authentication authentication = this.tokenProvider.getAuthentication(jwt);  //获取用户认证信息
    SecurityContextHolder.getContext().setAuthentication(authentication);   //将用户保存到SecurityContext
   }
   filterChain.doFilter(servletRequest, servletResponse);
  }catch (ExpiredJwtException e){          //JWT失效
   log.info("Security exception for user {} - {}",
     e.getClaims().getSubject(), e.getMessage());
   log.trace("Security exception trace: {}", e);
   ((HttpServletResponse) servletResponse).setStatus(HttpServletResponse.SC_UNAUTHORIZED);
  }
 }
 private String resolveToken(HttpServletRequest request){
  String bearerToken = request.getHeader(WebSecurityConfig.AUTHORIZATION_HEADER);   //从HTTP头部获取TOKEN
  if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")){
   return bearerToken.substring(7, bearerToken.length());        //返回Token字符串,去除Bearer
  }
  String jwt = request.getParameter(WebSecurityConfig.AUTHORIZATION_TOKEN);    //从请求参数中获取TOKEN
  if (StringUtils.hasText(jwt)) {
   return jwt;
  }
  return null;
 }
}

编写 LoginController,用户通过用户名、密码访问 /auth/login,通过 LoginDTO 对象接收,创建一个 Authentication 对象,代码中为 UsernamePasswordAuthenticationToken,判断对象是否存在,通过 AuthenticationManager 的 authenticate 方法对认证对象进行验证,AuthenticationManager 的实现类 ProviderManager 会通过 AuthentionProvider(认证处理) 进行验证,默认 ProviderManager 调用 DaoAuthenticationProvider 进行认证处理,DaoAuthenticationProvider 中会通过 UserDetailsService(认证信息来源) 获取 UserDetails ,若认证成功则返回一个包含权限的 Authention,然后通过 SecurityContextHolder.getContext().setAuthentication() 设置到 SecurityContext 中,根据 Authentication 生成 Token,并返回给用户

@RestController
public class LoginController {
 @Autowired
 private UserRepository userRepository;
 @Autowired
 private AuthenticationManager authenticationManager;
 @Autowired
 private JWTTokenUtils jwtTokenUtils;
 @RequestMapping(value = "/auth/login",method = RequestMethod.POST)
 public String login(@Valid LoginDTO loginDTO, HttpServletResponse httpResponse) throws Exception{
  //通过用户名和密码创建一个 Authentication 认证对象,实现类为 UsernamePasswordAuthenticationToken
  UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginDTO.getUsername(),loginDTO.getPassword());
  //如果认证对象不为空
  if (Objects.nonNull(authenticationToken)){
   userRepository.findByName(authenticationToken.getPrincipal().toString())
     .orElseThrow(()->new Exception("用户不存在"));
  }
  try {
   //通过 AuthenticationManager(默认实现为ProviderManager)的authenticate方法验证 Authentication 对象
   Authentication authentication = authenticationManager.authenticate(authenticationToken);
   //将 Authentication 绑定到 SecurityContext
   SecurityContextHolder.getContext().setAuthentication(authentication);
   //生成Token
   String token = jwtTokenUtils.createToken(authentication,false);
   //将Token写入到Http头部
   httpResponse.addHeader(WebSecurityConfig.AUTHORIZATION_HEADER,"Bearer "+token);
   return "Bearer "+token;
  }catch (BadCredentialsException authentication){
   throw new Exception("密码错误");
  }
 }
}

编写 Security 配置类,继承 WebSecurityConfigurerAdapter,重写 configure 方法

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
 public static final String AUTHORIZATION_HEADER = "Authorization";
 public static final String AUTHORIZATION_TOKEN = "access_token";
 @Autowired
 private UserDetailsService userDetailsService;
 @Override
 protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  auth
    //自定义获取用户信息
    .userDetailsService(userDetailsService)
    //设置密码加密
    .passwordEncoder(passwordEncoder());
 }
 @Override
 protected void configure(HttpSecurity http) throws Exception {
  //配置请求访问策略
  http
    //关闭CSRF、CORS
    .cors().disable()
    .csrf().disable()
    //由于使用Token,所以不需要Session
    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
    .and()
    //验证Http请求
    .authorizeRequests()
    //允许所有用户访问首页 与 登录
    .antMatchers("/","/auth/login").permitAll()
    //其它任何请求都要经过认证通过
    .anyRequest().authenticated()
    //用户页面需要用户权限
    .antMatchers("/userpage").hasAnyRole("USER")
    .and()
    //设置登出
    .logout().permitAll();
  //添加JWT filter 在
  http
    .addFilterBefore(genericFilterBean(), UsernamePasswordAuthenticationFilter.class);
 }
 @Bean
 public PasswordEncoder passwordEncoder() {
  return new BCryptPasswordEncoder();
 }
 @Bean
 public GenericFilterBean genericFilterBean() {
  return new JwtAuthenticationTokenFilter();
 }
}

编写用于测试的Controller

@RestController
public class UserController {
 @PostMapping("/login")
 public String login() {
  return "login";
 }
 @GetMapping("/")
 public String index() {
  return "hello";
 }
 @GetMapping("/userpage")
 public String httpApi() {
  System.out.println(SecurityContextHolder.getContext().getAuthentication().getPrincipal());
  return "userpage";
 }
 @GetMapping("/adminpage")
 public String httpSuite() {
  return "userpage";
 }
}

案例源码下载  (本地下载)

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • 详解Springboot整合Dubbo之代码集成和发布

    本文介绍了Springboot整合Dubbo之代码集成和发布,分享给大家,具体如下: 1. boot-dubbo-api相关 打开boot-dubbo-api项目,正在src/main/java下创建一个包,并创建你需要dubbo暴露的接口TestService.java,并创建一个实体类用于测试User.java.如下图所示: 创建文件和包结构 User.java package com.boot.domain; import lombok.Data; import java.io.Seria

  • Spring boot + mybatis + orcale实现步骤实例代码讲解

    接着上次的实现, 添加 mybatis 查询 orcale 数据库 第一步: 新建几个必须的包, 结果如下 第二步: 在service包下新建personService.java 根据名字查person方法接口 package com.example.first.service; import com.example.first.entity.Person; public interface personService { Person queryPersonByName(String name

  • 浅谈SpringCache与redis集成实现缓存解决方案

    缓存可以说是加速服务响应速度的一种非常有效并且简单的方式.在缓存领域,有很多知名的框架,如EhCache .Guava.HazelCast等.Redis作为key-value型数据库,由于他的这一特性,Redis也成为一种流行的数据缓存工具. 在传统方式下对于缓存的处理代码是非常臃肿的. 例如:我们要把一个查询函数加入缓存功能,大致需要三步. 一.在函数执行前,我们需要先检查缓存中是否存在数据,如果存在则返回缓存数据 二.如果不存在,就需要在数据库的数据查询出来. 三.最后把数据存放在缓存中,当

  • 浅谈spring中scope作用域

    今天研究了一下scope的作用域.默认是单例模式,即scope="singleton".另外scope还有prototype.request.session.global session作用域.scope="prototype"多例.再配置bean的作用域时,它的头文件形式如下: 如何使用spring的作用域: <bean id="role" class="spring.chapter2.maryGame.Role" s

  • 详解Spring Cloud Zuul 服务网关

    有了Eureka服务注册发现.Hystrix断路器.Ribbon服务调用负载均衡,以及spring cloud config 集群配置中心,似乎一个微服务框架已五脏俱全,last but not least,一个服务网关却不可或缺. Spring Cloud Zuul路由是微服务架构的不可或缺的一部分,提供动态路由,监控,弹性,安全等的边缘服务.Zuul是Netflix出品的一个基于JVM路由和服务端的负载均衡器. Zuul介绍 在整个Spring Cloud微服务框架里,Zuul扮演着"智能网

  • 使用Spring Cloud Feign上传文件的示例

    最近经常有人问Spring Cloud Feign如何上传文件.有团队的新成员,也有其他公司的兄弟.本文简单做个总结-- 早期的Spring Cloud中,Feign本身是没有上传文件的能力的(1年之前),要想实现这一点,需要自己去编写Encoder 去实现上传.现在我们幸福了很多.因为Feign官方提供了子项目feign-form ,其中实现了上传所需的 Encoder . 注:笔者测试的版本是Edgware.RELEASE.Camden.Dalston同样适应本文所述. 加依赖 <depen

  • 利用spring boot如何快速启动一个web项目详解

    前言 基于我们创建好的lion项目,使用spring boot,我们就可以通过很少的一些配置,便可以启动这个项目.下面话不多说了,来一起看看详细的介绍吧. 方法如下: 1 引入Spring boot,我们打开lion父模块的pom文件,继承 spring boot的pom 2让lion-web模块依赖spring boot的web相关的jar包,打开lion-web项目下的pom文件,添加如下的依赖 3 添加spring boot入口启动类Application.java,这个类要房子lion-

  • Springboot整合Dubbo教程之项目创建和环境搭建

    本文介绍了Springboot整合Dubbo教程之项目创建和环境搭建,分享给大家,具体如下: 1. 使用IDEA新建一个Maven项目 新建项目 选择Maven后,点击next下一步 选择项目类型 配置项目的Maven坐标 设置项目名称和保存位置 修改项目的pom.xml文件 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM

  • Spring Security结合JWT的方法教程

    概述 众所周知使用 JWT 做权限验证,相比 Session 的优点是,Session 需要占用大量服务器内存,并且在多服务器时就会涉及到共享 Session 问题,在手机等移动端访问时比较麻烦 而 JWT 无需存储在服务器,不占用服务器资源(也就是无状态的),用户在登录后拿到 Token 后,访问需要权限的请求时附上 Token(一般设置在Http请求头),JWT 不存在多服务器共享的问题,也没有手机移动端访问问题,若为了提高安全,可将 Token 与用户的 IP 地址绑定起来 前端流程 用户

  • SpringBoot集成Spring Security用JWT令牌实现登录和鉴权的方法

    最近在做项目的过程中 需要用JWT做登录和鉴权 查了很多资料 都不甚详细 有的是需要在application.yml里进行jwt的配置 但我在导包后并没有相应的配置项 因而并不适用 在踩过很多坑之后 稍微整理了一下 做个笔记 一.概念 1.什么是JWT Json Web Token (JWT)是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准(RFC 7519) 该token被设计为紧凑且安全的 特别适用于分布式站点的单点登录(SSO)场景 随着JWT的出现 使得校验方式更加简单便

  • Spring Security中用JWT退出登录时遇到的坑

    最近有个粉丝提了个问题,说他在Spring Security中用JWT做退出登录的时无法获取当前用户,导致无法证明"我就是要退出的那个我",业务失败!经过我一番排查找到了原因,而且这个错误包括我自己的大部分人都犯过. Session会话 之所以要说Session会话,是因为Spring Security默认配置就是有会话的,所以当你登录以后Session就会由服务端保持直到你退出登录.只要Session保持住,你的请求只要进入服务器就可以从 ServletRequest 中获取到当前的

  • Springboot集成Spring Security实现JWT认证的步骤详解

    1 简介 Spring Security作为成熟且强大的安全框架,得到许多大厂的青睐.而作为前后端分离的SSO方案,JWT也在许多项目中应用.本文将介绍如何通过Spring Security实现JWT认证. 用户与服务器交互大概如下: 客户端获取JWT,一般通过POST方法把用户名/密码传给server: 服务端接收到客户端的请求后,会检验用户名/密码是否正确,如果正确则生成JWT并返回:不正确则返回错误: 客户端拿到JWT后,在有效期内都可以通过JWT来访问资源了,一般把JWT放在请求头:一次

  • Spring Security 控制授权的方法

    本文介绍了Spring Security 控制授权的方法,分享给大家,具体如下: 使用授权方法进行授权配置 每一个 Spring Security 控制授权表达式(以下简称为表达式)实际上都在在 API 中对应一个授权方法,该方法是请求的 URL 权限配置时的处理方法.例如: @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers(

  • 使用Spring Security控制会话的方法

    1.概述 在本文中,我们将说明Spring Security如何允许我们控制HTTP会话.此控件的范围从会话超时到启用并发会话和其他高级安全配置. 2.会话何时创建? 我们可以准确控制会话何时创建以及Spring Security如何与之交互: •always - 如果一个会话尚不存在,将始终创建一个会话 •ifRequired - 仅在需要时创建会话(默认) •never - 框架永远不会创建会话本身,但如果它已经存在,它将使用一个 •stateless - Spring Security不会

  • Spring Boot 整合 JWT的方法

    1.JWT 是什么? JWT 是一个开放标准,它定义了一种用于简洁,自包含的用于通信双方之间以 JSON 对象的形式安全传递信息的方法.JWT 可以使用 HMAC 算法或者是 RSA 的公钥密钥对进行签名. 简单来说,就是通过一定规范来生成 token,然后可以通过解密算法逆向解密 token,这样就可以获取用户信息. 优点: 1)生产的 token 可以包含基本信息,比如 id.用户昵称.头像等信息,避免再次查库 2)存储在客户端,不占用服务端的内存资源 缺点: token 是经过 base6

  • Springboot WebFlux集成Spring Security实现JWT认证的示例

    1 简介 在之前的文章<Springboot集成Spring Security实现JWT认证>讲解了如何在传统的Web项目中整合Spring Security和JWT,今天我们讲解如何在响应式WebFlux项目中整合.二者大体是相同的,主要区别在于Reactive WebFlux与传统Web的区别. 2 项目整合 引入必要的依赖: <dependency> <groupId>org.springframework.boot</groupId> <art

  • spring security结合jwt实现用户重复登录处理

    目录 背景 方案 思路图 核心代码 背景 近日,客户针对我司系统做一些列漏洞扫描,最后总结通用漏洞有如下: 用户重复登录 接口未授权 接口越权访问 针对以上漏洞,分三篇文章分别记录解决方案,供后续回忆学习,本文先处理用户重复登录漏洞 方案 系统采用spring boot搭建,spring security+ jwt 作为安全框架 用户登录成功 生成token给到用户, 同时存储到redis中(key值为用户名(标识)) value为生成的token 用户再次访问系统请求参数中带有token信息

  • Spring Security基于JWT实现SSO单点登录详解

    SSO :同一个帐号在同一个公司不同系统上登陆 使用SpringSecurity实现类似于SSO登陆系统是十分简单的 下面我就搭建一个DEMO 首先来看看目录的结构 其中sso-demo是父工程项目 sso-client .sso-client2分别对应2个资源服务器,sso-server是认证服务器 引入的pom文件 sso-demo <?xml version="1.0" encoding="UTF-8"?> <project xmlns=&q

随机推荐