热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

SpringBoot整合SpringSecurity+JWT实现单点登录

SpringBoot整合SpringSecurity+JWT实现单点登录,Go语言社区,Golang程序员人脉社

SpringBoot整合SpringSecurity+JWT实现单点登录

    • 一、JSON Web Token(JWT)
    • 二、项目背景
    • 三、实现方式及效果
      • 1.登录拦截全局配置
      • 2.JwtToken解析并生成authentication身份信息过滤器
      • 3.自定义登录成功处理器并生成token:响应状态码200及token
      • 4.其他核心处理器没有变化,参考之前的博文:前后端分离 SpringBoot整合SpringSecurity权限控制(动态拦截url)
    • 四、测试
    • 附:GitHub


一、JSON Web Token(JWT)


官方解释:JSON Web Token(JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间作为JSON对象安全地传输信息。由于此信息是经过数字签名的,因此可以被验证和信任。可以使用秘密(使用HMAC算法)或使用RSA或ECDSA的公钥/私钥对对JWT进行签名。



二、项目背景

公司的业务越来越复杂,随着业务的扩展需要将现有单个后端web系统进行拆分,并在同为顶级域名的多个web系统之间实现单点登录及权限控制,同时也为以后的服务集群部署做准备。


三、实现方式及效果

实现的方式:基于现有的完整权限控制项目,之前整理过的一篇博文 前后端分离 SpringBoot整合SpringSecurity权限控制(动态拦截url),在此基础之上引入JWT实现单点登录。

实现的效果:


  • 用户不带token访问系统B,系统B响应状态码401(需要认证)

  • 用户登录系统A,系统A校验用户名密码成功,生成并响应token及状态码200

  • 用户没有登录系统B而是携带系统A响应的token去访问系统B

  • 系统B解析token并进行权限校验,无权限访问资源则响应403(权限不足),权限验证成功则响应正常的json数据

  • 访问系统C、系统D或分布式集群亦是如此

GitHub: link. 欢迎star

maven依赖:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>

1.登录拦截全局配置

@Configuration
public class WebSecurityConfigure extends WebSecurityConfigurerAdapter {
@Resource
private UrlAuthenticationEntryPoint authenticationEntryPoint; //自定义未登录时:返回状态码401
@Resource
private UrlAuthenticationSuccessHandler authenticationSuccessHandler; //自定义登录成功处理器并生成token:响应状态码200及token
@Resource
private UrlAuthenticationFailureHandler authenticationFailureHandler; //自定义登录失败处理器:返回状态码402
@Resource
private UrlAccessDeniedHandler accessDeniedHandler; //自定义权限不足处理器:返回状态码403
@Resource
private UrlLogoutSuccessHandler logoutSuccessHandler; //自定义注销成功处理器:返回状态码200
@Resource
private SelfAuthenticationProvider authenticationProvider; //自定义登录认证
@Resource
private SelfFilterInvocationSecurityMetadataSource filterInvocationSecurityMetadataSource; //动态获取url权限配置
@Resource
private SelfAccessDecisionManager accessDecisionManager; //自定义权限判断管理器
@Resource
private AuthenticationDetailsSource<HttpServletRequest, WebAuthenticationDetails> authenticationDetailsSource; //身份验证详细信息源
@Resource
private JwtAuthorizationTokenFilter authorizationTokenFilter; //JwtToken解析并生成authentication身份信息过滤器
@Override
public void configure(WebSecurity web) {
// web.ignoring().antMatchers("/connect/**"); //无条件允许访问
web.ignoring().antMatchers("/common/**"); //无条件允许访问
}
@Override
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(authenticationProvider); //自定义登录认证
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// 关闭csrf验证(防止跨站请求伪造攻击)
http.csrf().disable();
// JwtToken解析并生成authentication身份信息过滤器
http.addFilterBefore(authorizationTokenFilter, UsernamePasswordAuthenticationFilter.class);
// 未登录时:返回状态码401
http.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);
// 无权访问时:返回状态码403
http.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
// url权限认证处理
http.antMatcher("/**").authorizeRequests()
// .antMatchers("/security/user/**").hasRole("ADMIN") //需要ADMIN角色才可以访问
// .antMatchers("/connect").hasIpAddress("127.0.0.1") //只有ip[127.0.0.1]可以访问'/connect'接口
.anyRequest() //其他任何请求
.authenticated() //都需要身份认证
.withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
@Override
public <O extends FilterSecurityInterceptor> O postProcess(O o) {
o.setSecurityMetadataSource(filterInvocationSecurityMetadataSource); //动态获取url权限配置
o.setAccessDecisionManager(accessDecisionManager); //权限判断
return o;
}
});
// 将session策略设置为无状态的,通过token进行登录认证
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// 开启自动配置的登录功能
http.formLogin() //开启登录
// .loginPage("/login") //登录页面(前后端不分离)
.loginProcessingUrl("/nonceLogin") //自定义登录请求路径(post)
.usernameParameter("username").passwordParameter("password") //自定义登录用户名密码属性名,默认为username和password
// .successForwardUrl("/index") //登录成功后的url(post,前后端不分离)
// .failureForwardUrl("/error") //登录失败后的url(post,前后端不分离)
.successHandler(authenticationSuccessHandler) //验证成功处理器(前后端分离):生成token及响应状态码200
.failureHandler(authenticationFailureHandler) //验证失败处理器(前后端分离):返回状态码402
.authenticationDetailsSource(authenticationDetailsSource); //身份验证详细信息源(登录验证中增加额外字段)
// 开启自动配置的注销功能
http.logout() //用户注销, 清空session
.logoutUrl("/nonceLogout") //自定义注销请求路径
// .logoutSuccessUrl("/bye") //注销成功后的url(前后端不分离)
.logoutSuccessHandler(logoutSuccessHandler); //注销成功处理器(前后端分离):返回状态码200
}
}

2.JwtToken解析并生成authentication身份信息过滤器

@SuppressWarnings("unchecked")
@Slf4j
@Component
public class JwtAuthorizationTokenFilter extends OncePerRequestFilter {
@Value("${jwt.token-header-key}")
private String tokenHeaderKey; //token请求头Key
@Value("${jwt.token-prefix}")
private String tokenPrefix; //token前缀
@Value("${jwt.token-secret}")
private String tokenSecret; //token秘钥
/**
* 解析token并生成authentication身份信息
*/

@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
String token = request.getHeader(tokenHeaderKey);
log.info("JwtAuthorizationTokenFilter >> token:{}", token);
if (null == token || !token.startsWith(tokenPrefix + " ")) {
chain.doFilter(request, response);
return;
}
Claims claims;
try {
// 解析token
claims = Jwts.parser().setSigningKey(tokenSecret).parseClaimsJws(token.replace(tokenPrefix + " ", "")).getBody();
} catch (Exception e) {
log.error("JwtToken validity!! error={}", e.getMessage());
chain.doFilter(request, response);
return;
}
String username = claims.getSubject();
List<String> roles = claims.get("role", List.class);
List<SimpleGrantedAuthority> authorities = roles.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
if (null != username) {
// 生成authentication身份信息
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(username, null, authorities);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
chain.doFilter(request, response);
}
}

3.自定义登录成功处理器并生成token:响应状态码200及token

@Component
public class UrlAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Value("${jwt.token-header-key}")
private String tokenHeaderKey; //token响应头Key
@Value("${jwt.token-prefix}")
private String tokenPrefix; //token前缀
@Value("${jwt.token-secret}")
private String tokenSecret; //token秘钥
@Value("${jwt.token-expiration}")
private Long tokenExpiration; //token过期时间
@Resource
private UserService userService;
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
httpServletResponse.setCharacterEncoding("UTF-8");
UrlResponse response = new UrlResponse();
response.setSuccess(true);
response.setCode("200");
response.setMessage("Login Success!");
String username = (String) authentication.getPrincipal(); //表单输入的用户名
Map<String, Object> userInfo = userService.findMenuInfoByUsername(username, response); //用户可访问的菜单信息
response.setData(userInfo);
// 生成token并设置响应头
Claims claims = Jwts.claims();
claims.put("role", authentication.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()));
String token = Jwts.builder()
.setClaims(claims)
.setSubject(username) //设置用户名
.setExpiration(new Date(System.currentTimeMillis() + tokenExpiration)) //设置token过期时间
.signWith(SignatureAlgorithm.HS512, tokenSecret).compact(); //设置token签名算法及秘钥
httpServletResponse.addHeader(tokenHeaderKey, tokenPrefix + " " + token); //设置token响应头
httpServletResponse.getWriter().write(GsonUtil.GSON.toJson(response));
}
}

4.其他核心处理器没有变化,参考之前的博文:前后端分离 SpringBoot整合SpringSecurity权限控制(动态拦截url)

参考之前的博文或者github项目有详细的记录:前后端分离 SpringBoot整合SpringSecurity权限控制(动态拦截url)


四、测试

1.模拟多系统(或分布式集群)启动三个端口801880288038
在这里插入图片描述

2.没有登录也没有携带token访问系统A,响应401(需要认证)
在这里插入图片描述

3.在系统B的登录接口,响应头中得到了签名token
在这里插入图片描述

4.携带token访问系统A的获取所有用户信息的接口,此token没有该接口访问权限,响应403(权限不足)
在这里插入图片描述

5.在系统A登录具有获取所有用户信息权限的用户,并在响应头中获得签名token
在这里插入图片描述

6.此时携带具有权限的token,访问系统C的获取所用用户信息接口,系统C解析并校验成功,响应json数据
在这里插入图片描述


附:GitHub

GitHub: link. 欢迎star




推荐阅读
  • 视觉Transformer综述
    本文综述了视觉Transformer在计算机视觉领域的应用,从原始Transformer出发,详细介绍了其在图像分类、目标检测和图像分割等任务中的最新进展。文章不仅涵盖了基础的Transformer架构,还深入探讨了各类增强版Transformer模型的设计思路和技术细节。 ... [详细]
  • D17:C#设计模式之十六观察者模式(Observer Pattern)【行为型】
    一、引言今天是2017年11月份的最后一天,也就是2017年11月30日,利用今天再写一个模式,争取下个月(也就是12月份& ... [详细]
  • Vue CLI 基础入门指南
    本文详细介绍了 Vue CLI 的基础使用方法,包括环境搭建、项目创建、常见配置及路由管理等内容,适合初学者快速掌握 Vue 开发环境。 ... [详细]
  • 本文探讨了如何在 Spring MVC 框架下,通过自定义注解和拦截器机制来实现细粒度的权限管理功能。 ... [详细]
  • 本文探讨了Python类型注解使用率低下的原因,主要归结于历史背景和投资回报率(ROI)的考量。文章不仅分析了类型注解的实际效用,还回顾了Python类型注解的发展历程。 ... [详细]
  • 问题描述现在,不管开发一个多大的系统(至少我现在的部门是这样的),都会带一个日志功能;在实际开发过程中 ... [详细]
  • 本文回顾了作者在求职阿里和腾讯实习生过程中,从最初的迷茫到最后成功获得Offer的心路历程。文中不仅分享了个人的面试经历,还提供了宝贵的面试准备建议和技巧。 ... [详细]
  • Asynchronous JavaScript and XML (AJAX) 的流行很大程度上得益于 Google 在其产品如 Google Suggest 和 Google Maps 中的应用。本文将深入探讨 AJAX 在 .NET 环境下的工作原理及其实现方法。 ... [详细]
  • Python3爬虫入门:pyspider的基本使用[python爬虫入门]
    Python学习网有大量免费的Python入门教程,欢迎大家来学习。本文主要通过爬取去哪儿网的旅游攻略来给大家介绍pyspid ... [详细]
  • H5技术实现经典游戏《贪吃蛇》
    本文将分享一个使用HTML5技术实现的经典小游戏——《贪吃蛇》。通过H5技术,我们将探讨如何构建这款游戏的两种主要玩法:积分闯关和无尽模式。 ... [详细]
  • 在尝试加载支持推送通知的iOS应用程序的Ad Hoc构建时,遇到了‘no valid aps-environment entitlement found for application’的错误提示。本文将探讨此错误的原因及多种可能的解决方案。 ... [详细]
  • 本文详细介绍了如何在Spring框架中设置事件发布器、定义事件监听器及响应事件的具体步骤。通过实现ApplicationEventPublisherAware接口来创建事件发布器,利用ApplicationEvent类定义自定义事件,并通过ApplicationListener接口来处理这些事件。 ... [详细]
  • 长期从事ABAP开发工作的专业人士,在面对行业新趋势时,往往需要重新审视自己的发展方向。本文探讨了几位资深专家对ABAP未来走向的看法,以及开发者应如何调整技能以适应新的技术环境。 ... [详细]
  • 本文探讨了如何通过优化 DOM 操作来提升 JavaScript 的性能,包括使用 `createElement` 函数、动画元素、理解重绘事件及处理鼠标滚动事件等关键主题。 ... [详细]
  • OBS Studio自动化实践:利用脚本批量生成录制场景
    本文探讨了如何利用OBS Studio进行高效录屏,并通过脚本实现场景的自动生成。适合对自动化办公感兴趣的读者。 ... [详细]
author-avatar
浪漫的白狼族
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有