带有Spring Cloud Microservices的JSON Web令牌

在Keyhole,我们已经发布了几个有关微服务的博客 。 我们已经讨论了微服务环境中使用的架构模式,例如服务发现和断路器 。 我们甚至在平台和工具上发布了博客,例如最近关于Service Fabric的博客 。

我们已经介绍过的架构的重要组成部分是围绕微服务的安全性。 具体来说,是身份验证和授权模式。

在考虑微服务中的身份验证时,有多种选择,但是此博客将特别关注使用JSON Web令牌。

JSON Web令牌

本质上,JSON Web令牌(JWT)是一个独立的身份验证令牌,可以包含诸如用户标识符,用户的角色和权限以及您可能希望存储在其中的任何其他信息。 任何人都可以轻松读取和解析它,并可以使用密钥验证其真实性。 有关JSON Web令牌的简要介绍,请查看此页面 。

将JSON Web令牌与微服务一起使用的一个优势是,我们可以对其进行设置,使其已经包含用户拥有的所有权限。 这意味着每个服务都无需伸手我们的授权服务即可对用户进行授权。

JWT的另一个优点是它们是可序列化的,并且足够小以适合请求标头。

怎么运行的

工作流程非常简单。 第一个请求是使用用户名和密码的POST到不受保护的身份验证端点。

成功认证后,响应将包含JWT。 所有其他请求都带有一个HTTP标头,其中包含以Authorization: xxxxx.yyyyy.zzzzz形式的JWT令牌Authorization: xxxxx.yyyyy.zzzzz

任何服务到服务的请求都将传递此标头,以便任何服务都可以沿途应用授权。

现在,到代码!

我们需要做的第一件事是弄清楚如何生成这些JWT。 幸运的是,我们不是第一个尝试此操作的人,并且有多个库可供选择。

我选择了Java JWT 。 这是我的实现:

public class JsonWebTokenUtility {private SignatureAlgorithm signatureAlgorithm;private Key secretKey;public JsonWebTokenUtility() {// THIS IS NOT A SECURE PRACTICE!// For simplicity, we are storing a static key here.// Ideally, in a microservices environment, this key would kept on a// config server.signatureAlgorithm = SignatureAlgorithm.HS512;String encodedKey = "L7A/6zARSkK1j7Vd5SDD9pSSqZlqF7mAhiOgRbgv9Smce6tf4cJnvKOjtKPxNNnWQj+2lQEScm3XIUjhW+YVZg==";secretKey = deserializeKey(encodedKey);}public String createJsonWebToken(AuthTokenDetailsDTO authTokenDetailsDTO) {String token = Jwts.builder().setSubject(authTokenDetailsDTO.userId).claim("email", authTokenDetailsDTO.email).claim("roles", authTokenDetailsDTO.roleNames).setExpiration(authTokenDetailsDTO.expirationDate).signWith(getSignatureAlgorithm(), getSecretKey()).compact();return token;}private Key deserializeKey(String encodedKey) {byte[] decodedKey = Base64.getDecoder().decode(encodedKey);Key key = new SecretKeySpec(decodedKey, getSignatureAlgorithm().getJcaName());return key;}private Key getSecretKey() {return secretKey;}public SignatureAlgorithm getSignatureAlgorithm() {return signatureAlgorithm;}public AuthTokenDetailsDTO parseAndValidate(String token) {AuthTokenDetailsDTO authTokenDetailsDTO = null;try {Claims claims = Jwts.parser().setSigningKey(getSecretKey()).parseClaimsJws(token).getBody();String userId = claims.getSubject();String email = (String) claims.get("email");List roleNames = (List) claims.get("roles");Date expirationDate = claims.getExpiration();authTokenDetailsDTO = new AuthTokenDetailsDTO();authTokenDetailsDTO.userId = userId;authTokenDetailsDTO.email = email;authTokenDetailsDTO.roleNames = roleNames;authTokenDetailsDTO.expirationDate = expirationDate;} catch (JwtException ex) {System.out.println(ex);}return authTokenDetailsDTO;}private String serializeKey(Key key) {String encodedKey = Base64.getEncoder().encodeToString(key.getEncoded());return encodedKey;}}

现在我们有了这个实用程序类,我们需要在我们的每个微服务中设置Spring Security。

为此,我们将需要一个自定义身份验证过滤器,该过滤器将读取请求标头(如果存在)。 Spring中已经有一个身份验证过滤器,它已经执行了此操作,称为RequestHeaderAuthenticationFilter ,我们可以对其进行扩展。

public class JsonWebTokenAuthenticationFilter extends RequestHeaderAuthenticationFilter {public JsonWebTokenAuthenticationFilter() {// Don't throw exceptions if the header is missingthis.setExceptionIfHeaderMissing(false);// This is the request header it will look forthis.setPrincipalRequestHeader("Authorization");}@Override@Autowiredpublic void setAuthenticationManager(AuthenticationManager authenticationManager) {super.setAuthenticationManager(authenticationManager);}
}

此时,标头已以PreAuthenticatedAuthenticationToken的形式转换为Spring Authentication对象。

现在,我们需要一个身份验证提供程序,它将读取此令牌,对其进行身份验证并将其转换为我们自己的自定义Authentication对象。

public class JsonWebTokenAuthenticationProvider implements AuthenticationProvider {private JsonWebTokenUtility tokenService = new JsonWebTokenUtility();@Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {Authentication authenticatedUser = null;// Only process the PreAuthenticatedAuthenticationTokenif (authentication.getClass().isAssignableFrom(PreAuthenticatedAuthenticationToken.class)&& authentication.getPrincipal() != null) {String tokenHeader = (String) authentication.getPrincipal();UserDetails userDetails = parseToken(tokenHeader);if (userDetails != null) {authenticatedUser = new JsonWebTokenAuthentication(userDetails, tokenHeader);}} else {// It is already a JsonWebTokenAuthenticationauthenticatedUser = authentication;}return authenticatedUser;}private UserDetails parseToken(String tokenHeader) {UserDetails principal = null;AuthTokenDetailsDTO authTokenDetails = tokenService.parseAndValidate(tokenHeader);if (authTokenDetails != null) {List<GrantedAuthority> authorities = authTokenDetails.roleNames.stream().map(roleName -> new SimpleGrantedAuthority(roleName)).collect(Collectors.toList());principal = new User(authTokenDetails.email, "", authorities);}return principal;}@Overridepublic boolean supports(Class<?> authentication) {return authentication.isAssignableFrom(PreAuthenticatedAuthenticationToken.class)|| authentication.isAssignableFrom(JsonWebTokenAuthentication.class);}}

有了这些组件,我们现在就可以连接标准的Spring Security以使用JWT。 进行服务到服务的呼叫时,我们将需要传递JWT。

我使用了Feign客户端,将JWT作为参数传递。

@FeignClient("user-management-service")
public interface UserManagementServiceAPI {@RequestMapping(value = "/authenticate", method = RequestMethod.POST)AuthTokenDTO authenticateUser(@RequestBody AuthenticationDTO authenticationDTO);@RequestMapping(method = RequestMethod.POST, value = "/roles")RoleDTO createRole(@RequestHeader("Authorization") String authorizationToken, @RequestBody RoleDTO roleDTO);@RequestMapping(method = RequestMethod.POST, value = "/users")UserDTO createUser(@RequestHeader("Authorization") String authorizationToken, @RequestBody UserDTO userDTO);@RequestMapping(method = RequestMethod.DELETE, value = "/roles/{id}")void deleteRole(@RequestHeader("Authorization") String authorizationToken, @PathVariable("id") int id);@RequestMapping(method = RequestMethod.DELETE, value = "/users/{id}")void deleteUser(@RequestHeader("Authorization") String authorizationToken, @PathVariable("id") int id);@RequestMapping(method = RequestMethod.GET, value = "/roles")Collection<RoleDTO> findAllRoles(@RequestHeader("Authorization") String authorizationToken);@RequestMapping(method = RequestMethod.GET, value = "/users")Collection<UserDTO> findAllUsers(@RequestHeader("Authorization") String authorizationToken);@RequestMapping(method = RequestMethod.GET, value = "/roles/{id}", produces = "application/json", consumes = "application/json")RoleDTO findRoleById(@RequestHeader("Authorization") String authorizationToken, @PathVariable("id") int id);@RequestMapping(method = RequestMethod.GET, value = "/users/{id}", produces = "application/json", consumes = "application/json")UserDTO findUserById(@RequestHeader("Authorization") String authorizationToken, @PathVariable("id") int id);@RequestMapping(method = RequestMethod.GET, value = "/users/{id}/roles")Collection<RoleDTO> findUserRoles(@RequestHeader("Authorization") String authorizationToken,@PathVariable("id") int id);@RequestMapping(method = RequestMethod.PUT, value = "/roles/{id}")void updateRole(@RequestHeader("Authorization") String authorizationToken, @PathVariable("id") int id,@RequestBody RoleDTO roleDTO);@RequestMapping(method = RequestMethod.PUT, value = "/users/{id}")void updateUser(@RequestHeader("Authorization") String authorizationToken, @PathVariable("id") int id,@RequestBody UserDTO userDTO);
}

为了传递JWT,我只是从Spring Security的控制器中抓取了它,如下所示:

private String getAuthorizationToken() {String token = null;Authentication authentication = SecurityContextHolder.getContext().getAuthentication();if (authentication != null && authentication.getClass().isAssignableFrom(JsonWebTokenAuthentication.class)) {JsonWebTokenAuthentication jwtAuthentication = (JsonWebTokenAuthentication) authentication;token = jwtAuthentication.getJsonWebToken();}return token;
}

如您所知,JWT非常适合于分布式微服务环境,并提供多种功能。 在为下一个微服务项目设计安全体系结构时,请考虑JSON Web令牌。

翻译自: https://www.javacodegeeks.com/2016/06/json-web-tokens-spring-cloud-microservices.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/352684.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

STC用PCA测量脉宽_教你测量玉手镯圈号及如何轻松快速摘戴玉手镯?

一、如何测量玉手镯的圈号&#xff1f;测量和田玉手镯的圈号并不复杂&#xff0c;自己在家就能轻松搞定哦&#xff01;共有两种方法可选。方法一&#xff1a;游标卡尺法所需工具&#xff1a;游标卡尺具体方法&#xff1a;如照片所示&#xff0c;使用游标卡尺测量手掌最宽处(大拇…

Python中的函数(一)

接触过C语言的朋友对函数这个词肯定非常熟悉&#xff0c;无论在哪门编程语言当中&#xff0c;函数&#xff08;当然在某些语言里称作方法&#xff0c;意义是相同的&#xff09;都扮演着至关重要的角色。今天就来了解一下Python中的函数用法。 一.函数的定义 在某些编程语言当中…

Shell基础命令

它又是一种程序设计语言。作为命令语言&#xff0c;它交互式解释和执行用户输入的命令或者自动地解释和执行预先设定好的一连串的命令&#xff1b;作为程序设计语言&#xff0c;它定义了各种变量和参数&#xff0c;并提供了许多在高级语言中才具有的控制结构&#xff0c;包括循…

电脑报警5声_电脑故障怎么判断 常见电脑故障诊断方法介绍【详解】

在电脑使用的过程&#xff0c;出现一些电脑故障是在所难免的&#xff0c;很多小伙伴对一些常见电脑故障诊断的方法不是很了解&#xff0c;不知道自己电脑出现的这些故障 究竟是什么原因造成的 。关于软件故障有很多种可能性&#xff0c;一般都是比较容易解决的&#xff0c;今天…

洛谷P3857 [TJOI2008]彩灯(线性基)

传送门 线性基裸题 直接把所有的状态都带进去建一个线性基 然后答案就是$2^{cnt}$&#xff08;$cnt$代表线性基里数的个数&#xff09; 1 //minamoto2 #include<cstdio>3 #include<cstring>4 #define ll long long5 const int N55;6 ll b[N],a[N];int n,m,cnt;char…

sort函数

sort函数:#include <algorithm>,默认从小到大&#xff0c;如果降序可写第三方函数进行排序&#xff0c;EXP:sort(array,arrayn,cmp) 1.普通排序,升序 01#include <iostream> 02#include <algorithm> 03using namespace std; 04int main() 05{ 06 int a[10]{…

javaone_JavaOne 2012:非阻塞数据结构如何工作?

javaone当我查看今天的日程安排时&#xff0c;我感到有些惊讶&#xff0c;并指出我目前计划今天参加的所有会议都在希尔顿举行。 当我意识到JavaOne演示文稿中大约有一半是在希尔顿酒店中并且似乎按路线大致定位时&#xff0c;这变得有些不足为奇了。 Tobias Lindaaker &#x…

台式电脑键盘字母乱了_电脑键盘上的一个不起眼的按键,特别实用却被粗心的人忽略...

笔记本电脑与台式电脑的键盘有一个小小的区别。笔记本电脑没有输入数字的小键盘&#xff0c;而台式电脑键盘有&#xff1b;笔记本电脑键盘有一个Fn键&#xff0c;而台式电脑键盘没有。正是笔记本电脑键盘有了这个Fn键&#xff0c;为我们使用者提供了许多特别实用的功能&#xf…

ubuntu下crontab启动,重启,关闭命令

启动&#xff1a;/etc/init.d/cron start ( service cron start )重启&#xff1a;/etc/init.d/cron restart ( service cron restart )关闭&#xff1a;/etc/init.d/cron stop ( service cron stop )转载于:https://www.cnblogs.com/yu-yuan/p/9722202.html

NET Framework 4.0 安装失败:安装时发生严重错误

NET Framework 4.0 安装失败&#xff1a;安装时发生严重错误 看日志&#xff0c;好像是缺少系统组件。不知道你的系统是怎么装的&#xff0c;有耐心的话&#xff0c;请用系统盘执行Windows的默认方式安装。即常说的硬装&#xff0c;不要用网上常用的ghost方式安装。执行完硬装后…

C语言文件操作 fopen, fclose, mkdir(打开关闭文件,建文件夹,判断文件是否存在可读或可写)

1.建文件夹 int _mkdir(const char *path,mode_t mode); 函数名: _mkdir    功 能: 建立一个目录    用 法: int _mkdir( const char *dirname );    头文件库&#xff1a;direct.h    返回值&#xff1a;创建一个目录&#xff0c;若成功则返回0&#xff0c;否则返回-1…

差分进化算法_OPTIMUS软件功能特性介绍【全局优化算法模块】

导读&#xff1a;面向应用工程师的商业软件咨询、自研软件定制开发服务的仿真公众号&#xff0c;点击关注进入菜单&#xff0c;查看更多精彩内容。OPTIMUS提供自适应进化算法(Self-adaptive Evolution)&#xff0c;从用户给定的起始解或者算法随机产生的起始种群开始&#xff0…

Java生产力提示:社区的热门选择

社区已经发言。 我们已将您最好和最出色的生产力技巧汇总到一篇文章中。 我们都有自己的小技巧&#xff0c;可以帮助我们提高工作效率&#xff0c;并提高生产率。 我们使用工具来避免繁琐的日常任务&#xff0c;并运行脚本来自动化流程。 我们所做的一切只是为了确保一切就绪&…

JAVA获取Classpath根路径的方法

方法一&#xff1a; String path Test.class.getResource("/").toString(); System.out.println("path " path); 此方法在tomcat下面没有问题&#xff0c;可以取到WEB-INF/classes path file:/home/ngidm_db2/AS_Tomcat7_0_29/webapps/NGIDM/WEB-INF/c…

Navicat 9.1、10.0 简体中文最新版,注册码(For Mysql)

Navicat 9.1、10.0 简体中文最新版&#xff0c;注册码(For Mysql) by 尘缘 on 七月 17th, 2011 // Filed Under → MySQL Navicat属于偶的必备开发工具&#xff0c;最新版的自动提示&#xff0c;SQL格式化比较好用。 今天测试过Navicat 9.1.11&#xff0c;注册码可以使用。 下…

win10休眠设置_电脑总是“打瞌睡”?教你如何简单关闭自动休眠

电脑休眠指的是将当前处于运行状态的数据保存在硬盘中&#xff0c;整机完全停止供电。通常电脑的自动休眠是有一个电源计划的&#xff0c;电脑如果在设定的时间内没有使用&#xff0c;那么就会进入自动休眠状态。然而许多人长时间不操作电脑&#xff0c;只是想让电脑继续执行一…

nohup 命令 用途:不挂断地运行命令

简单而有用的nohup命令在UNIX/LINUX中&#xff0c;普通进程用&符号放到后台运行&#xff0c;如果启动该程序的控制台logout&#xff0c;则该进程随即终止。要实现守护进程&#xff0c;一种方法是按守护进程的规则去编程&#xff08;本站有文章介绍过&#xff09;&#xff0…

让VC编译的程序链接到系统的 msvcrt.dll 的方法

安装 DDK 或者 WDK &#xff0c;提取里面的 msvcrt.lib 和 msvcrtd.lib &#xff0c;这两个文件分别对应 release 和 debug 版本。 然后再按下图进行设置&#xff1a; 编译生成的程序&#xff0c;就直接链接系统的 msvcrt.dll 上了&#xff0c;debug 版是链接到 msvcrtd.dll。

局域网arp攻击_网络安全基础之ARP攻击和防御

本文转载于 SegmentFault 社区作者&#xff1a;吴小风前言在看这篇文章之前&#xff0c;请大家先看下交换机的工作原理&#xff0c;不知大家有没有想过数据链路层中头部协议数据帧的目的MAC地址是如何获取的呢&#xff1f;这就是今天的主角ARP协议&#xff0c;通过广播来获取IP…

SocketServer模块,hmac模块验证client合法性

hmac模块&#xff1a; 1.模块初识&#xff1a; import hmac # h hmac.new() #括号里要给它连个bytes类型&#xff0c;一个是自定义的secret_key&#xff0c;一个是你想进行加密的bytes # 密文 h.digest() # hmac.compare_digest() #括号内传另外一个密文&#xff0c;看是否相…