java jwks_Java SpringBoot 如何使用 IdentityServer4 作为验证服务器学习笔记
这边记录下如何使用IdentityServer4 作为 Java SpringBoot 的 认证服务器和令牌颁发服务器。本人也是新手,所以理解不足的地方请多多指教。另外由于真的很久没有写中文了,用词不太恰当的地方也欢迎新手大佬小伙伴指出,一起进步。另外这边令牌的获取需要手动使用postman根据令牌端点获取,然后放在请求头里面通过postman发给Java的demo,本身这个demo没有取令牌的功能,请各位注意。
背景知识:什么是JWT
第一部分:IdentityServer4的服务器搭建
第二部分:Java SpringBoot框架和IDS4的结合
1.什么是Jwt?
关于什么是Jwt,包括里面的参数是什么,这边可以参考下面这个链接做一些了解:
下面这个链接是全英文的,但是对jwt是什么是比较详细的,英文好的同学可以上了。
这个链接主要说了jwt和refernce token不一样,真的很重要,里面也有些说的不对,我就被坑了:
2.IdentityServer认证&令牌颁发服务器准备
关于IdentityServer4怎么搭建使用,网上已经有太多的教程了,这边我就不多做别的讲解,因为我也是新手。但是我目前自己正在使用的是一个带UI界面的IdentityServer4和Identity(作为用户管理的部分)结合的服务器,很多东西已经帮你搭建好了,对新手可以说是十分友好,省去了探索的步骤。但是不建议新手直接使用,作为自己搭建IdentityServer后还有对IdentitySevrer4一些参数不太理解的地方,可以做进一步的理解。下面是IdentityServer4 UI 的github源码链接:
这边要是有人感兴趣配置这个IdentityServer4 UI,后期也会记录下相对的这个事怎么搭建的。这个IdentityServer上面在配置相关的信息,比如API,Client,还有用户资源之后,我们会用到如下端点:
http://x.x.x.x:5000/connect/token请求令牌的端口,需要提供客户端id,客户端secret,用户名字,用户密码,还有授权方式,这里我选的grant_type是 password。
http://x.x.x.x:5000/connect/introspect令牌自省端点,很多国内的说法是用于refenrece token,然后还有很多大佬翻译的官方文档根本就是没认真翻译(估计也不知道实际意思)。这个端点实际上可以用于那些没由相应的包或者library可以用于解析jwt令牌的程序来验证令牌的合法性。只是注意这边唯一不同的是,对于renfence token和jwt token 要发给这个端点的参数是不一样的。对于reference,要发的是 client_id 和 secret。但是对于 jwt token,要发的是 base64编码在请求头部的 Api_name 和 Api_secret, 这里就是为什么 Api有secret这个参数,但是我们几乎没有用到过。
http://x.x.x.x:5000/.well-known/openid-configuration/jwks (公钥开放端点) 用于获取解析jwt令牌的公钥开放端点。
3.Java SpringBoot
关于如何开启一个新的项目,这边就不多说了,网上教程很多,我们直接进入正题,这边我用的Intellij IDEA。然后注册了过滤器,然后这边提供了两种办法验证jwt:
通过自省端点返回验证结果,使用http://x.x.x.x:5000/connect/introspect
通过公钥开放端点本地解析token,使用http://x.x.x.x:5000/.well-known/openid-configuration/jwks
由于没有客户端,这边用postman代替求取token,使用http://x.x.x.x:5000/connect/token,然后给我们的java程序发起请求。
3.1通过自省端点返回验证结果
这个就十分简单了,上代码,主要是Filter里面dofiler的部分:
@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throwsIOException, ServletException {
System.out.println("开始进行过滤请求,由认证服务器自省端点验证token");boolean authenticated = false;
HttpServletRequest req=(HttpServletRequest) servletRequest;
HttpServletResponse rep=(HttpServletResponse) servletResponse;//--------------给自省端点发送请求-------------------------------//--------------准备请求信息----------------------------------------//其实一个url请求就是一组组键对值,getHeader()方法获取的是头部的你想要的//键名后面的值,由于请求里面token的keyname是这个,倒是要是要改这里也要改//这里面header要是没有token这个就不行,会异常
boolean authorizationHeaderExist = req.getHeader("Authorization") != null;if (!authorizationHeaderExist) {
rep.setStatus(HttpServletResponse.SC_BAD_REQUEST);return;
}
String token= cutToken(req.getHeader("Authorization"));//获取编码后的ApiSecret和ApiName,在application.propertiesz中
String apiNameSecret = "Basic " +ApiNameSecretbase64();//倒是可以放到配置里面去,那里统一改
String introspectEndpoint = "http://localhost:5000/connect/introspect";//-------------创造请求----------------------------------------------//protected HttpClient client = new DefaultHttpClient();已过时
HttpClient client =HttpClientBuilder.create().build();
HttpPost post= newHttpPost(introspectEndpoint);//添加请求头
post.setHeader("Authorization", apiNameSecret);//添加请求主体(body)
List urlBodys = new ArrayList();
urlBodys.add(new BasicNameValuePair("token", token));
post.setEntity(newUrlEncodedFormEntity((urlBodys)));
HttpResponse response=client.execute(post);
System.out.println("\nSending 'POST' request to URL : " +introspectEndpoint);
System.out.println("Post parameters : " +post.getEntity());
System.out.println("Response Code : " +response.getStatusLine().getStatusCode());//读取返回reponse的content的信息,含有决定结果
BufferedReader rd = newBufferedReader(newInputStreamReader(response.getEntity().getContent()));//注意StringBuffer不是String
StringBuffer result = newStringBuffer();
String line= "";while ((line = rd.readLine()) != null) {
result.append(line);
}//调试用,打印得到的请求的content
System.out.println(result.toString());//-------------------------------决定authenticated结果---------------------------
JSONObject jo = newJSONObject(result.toString());
Boolean active= jo.getBoolean("active");if (response.getStatusLine().getStatusCode() == 200&& active==true)
{
String role= jo.getString("role");
authenticated= true;
}//--------------------------------处理authenticated结果,决定是否发出401-----------
if(authenticated)
{//调用该方法后,表示过滤器经过原来的url请求处理方法
filterChain.doFilter(servletRequest, servletResponse);
}else{
rep.setStatus(HttpServletResponse.SC_UNAUTHORIZED);return;
}
}//返回Api名字和secret的编码,请求头的一部分
publicString ApiNameSecretbase64()
{
String result= api.getapiName()+":"+api.getapiSecret();byte[] data=Base64.encodeBase64(result.getBytes());return newString(data);
}//处理token字符串,去掉Bearer
publicString cutToken(String originToken)
{
String[] temp= originToken.split(" ");return temp[1];
}
上面的 ApiNameSecretbase64 的功能是读取配置中的信息,返回编码好的 Api Name 和 Secret, 下面是我application.properties相关的配置,同样这些配置需要放到IdentityServer那边,可以是通过内存的方式也可以是通过像我一样使用 UI管理界面直接添加:
#IdentityServer4 配置文件参数
api.name=Api1
api.secret=secreta
同时启动java项目和IdentityServer4之后,请求就可以看到结果了,如果没有带token就会是无授权,这边就不放postman的结果了,因为本篇主要侧重于代码。关于请求这个端点的效果,返回格式参考官方文档,各位自己可以做本地相应的基于回复的验证方式。我这边直接提取了里面active这个布尔量,来确定这个令牌是否合法。
3.2通过请求公钥本地解析返回验证token
废话不多说先上代码:
@Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throwsIOException, ServletException
{
System.out.println("开始进行过滤请求,由认证服务器jwk公钥解析验证token");boolean authenticated = false;
HttpServletRequest req=(HttpServletRequest) servletRequest;
HttpServletResponse rep=(HttpServletResponse) servletResponse;boolean authorizationHeaderExist = req.getHeader("Authorization") != null;if (!authorizationHeaderExist) {
rep.setStatus(HttpServletResponse.SC_BAD_REQUEST);return;
}
String jwkEndpoint= "http://localhost:5000/.well-known/openid-configuration/jwks";//String token = cutToken(((HttpServletRequest) servletRequest).getHeader("Authorization"));
String token = cutToken(req.getHeader("Authorization"));//------------解析------------------------------------------------------//com.nimbusds JWT解析包,这个包目前没有找到源代码,//https://connect2id.com/products/nimbus-jose-jwt/examples/validating-jwt-access-tokens
//建立解析处理对象
ConfigurableJWTProcessor jwtProcessor = newDefaultJWTProcessor();//提供公钥地址来获取
JWKSource keySource = new RemoteJWKSet(newURL(jwkEndpoint));//提供解析算法,算法类型要写对,服务器用的是什么就是什么,目前是RSA256算法
JWSAlgorithm expectedJWSAlg =JWSAlgorithm.RS256;//填写 RSA 公钥来源从提供公钥地址获取那边得到
JWSKeySelector keySelector = newJWSVerificationKeySelector(expectedJWSAlg, keySource);if(keySelector==null)
{
rep.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
System.out.println("无法获取公钥。");return;
}//设置第一步建立的解析处理对象
jwtProcessor.setJWSKeySelector(keySelector);//处理收到的token(令牌),错误则返回对象
SecurityContext ctx = null;
JWTClaimsSet claimsSet= null;try{
claimsSet=jwtProcessor.process(token, ctx);
authenticated= true;
}catch(ParseException e) {
rep.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
e.printStackTrace();return;
}catch(BadJOSEException e) {
rep.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
e.printStackTrace();return;
}catch(JOSEException e) {
rep.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
e.printStackTrace();return;
}//调试用,打印出来
System.out.println(claimsSet.toJSONObject());//失败返回无授权
if(claimsSet==null) {
rep.setStatus(HttpServletResponse.SC_UNAUTHORIZED);return;
}//解码里面具体内容,尤其角色,虽然这里不需要,顺利取出
JSONObject jo = newJSONObject(claimsSet.toJSONObject());
String role= jo.getString("role");//试一下过期的token,删除用户的可以不试试//--------------------------------处理authenticated结果,决定是否发出401-----------
if(authenticated)
{//调用该方法后,表示过滤器经过原来的url请求处理方法
filterChain.doFilter(servletRequest, servletResponse);
}else{
rep.setStatus(HttpServletResponse.SC_UNAUTHORIZED);return;
}
}//帮助类
publicString cutToken(String originToken)
{
String[] temp= originToken.split(" ");return temp[1];
}
这边主要是用到了一个包,用法链接如下,英文好的同学可以直接研究这个链接:
https://connect2id.com/products/nimbus-jose-jwt/examples/validating-jwt-access-tokens
这个包需要import的东西还要maven依赖如下:
com.nimbusds
nimbus-jose-jwt
7.3
import com.nimbusds.jose.*;import com.nimbusds.jose.jwk.source.*;import com.nimbusds.jwt.*;importcom.nimbusds.jose.proc.JWSKeySelector;importcom.nimbusds.jose.proc.JWSVerificationKeySelector;import com.nimbusds.jwt.proc.*;
差不多是这样了,还有不是很清楚的地方直接看源代码。
相关文章:
git 快速复制一个新项目
gitlab创建项目a及一个主分支master 本地整体复制已有项目b 本地克隆gitlab上新创建的项目a(git clone gitlab地址) 将本地克隆过来的a文件夹下的.git目录拷贝到b项目下(cp -r .git …/b,注意是两个点,不是三个点,写两…
微信公众平台消息接口星标功能
【微信公众平台星标功能接口被撤销】微信公众平台消息接口中的星标功能,被悄悄的去掉了。 原因应该是有的账号在程序中大量使用星标功能,造成微信服务器存储记录过于宠大。 现在要继续使用星标功能,只能在后台手工操作。 <xml> <ToU…
国庆双节长假旅游出行必装的手机软件
长假即将来临,如果打算出去溜达溜达,透透气的朋友,本文可千万别错过了。今天将介绍几款应用是旅途中绝对不能缺少的,这些应用都非常好用、功能全面,是出行必备的手机软件,希望可以伴你度过快乐的十一长假。…
Windbg双机调试环境配置(Windows7/Windows XP+VirtualBox/VMware+WDK7600)
简介:Windbg双机调试内核、驱动 下载软件: 下载Windbg(GRMWDK_EN_7600_1.ISO)下载VirtualBox 5.2/VMware 12一、安装WDK,这里要提一点的是Debugging Tools for Windows一定要打勾,因为我们后面就是要通过这个工具来进行双机调试的…
哈夫曼树的java实现_java实现哈夫曼树
哈夫曼译码,就是将输入的译码还原成对应的字符。 抽象的算法描述:将建立哈夫曼树、实现哈夫曼编码、哈夫曼译码都定义成 子函数的的形式, 然后在主函数中调用它们......数据结构课程设计设计题目: 哈夫曼树及其应用学 院:计算机科学与技术 专业:网络...用哈夫曼树实现图像压缩_…
on-my-zsh git 仓库下运行卡顿
在 oh-my-zsh 进入 包含 git 仓库目录时,执行 ls 时会比较卡顿 原因: oh-my-zsh 要获取 git 更新信息 解决办法: 设置 oh-my-zsh 不读取文件变化信息(在 git 项目目录执行下列命令) $ git config --add oh-my-zsh…
oracle, group by, having, where
选择列表中如果包含有列、表达式时,这个列、表达式必须包含在Group By子句中。另外,如果采用了表达式的话,则数据库管理员即使在选择列表中采用了别名,但是在Group By子句中仍然必须采用表达式的完整表达方式,而不能够…
[转载] CSS模块化【封装-继承-多态】
第一次听到“CSS模块化”这个词是在WebReBuild的第四届“重构人生”年会上,当时我还想,“哈,CSS也有模块化,我没听错吧?”事实上,我没听错,你也没看错,早就有CSS模块化这个概念了。之…
用jQuery写的一个翻页,并封装为插件,
用jQuery写的一个翻页,并封装为插件, 1 *{2 margin:0;3 padding: 0;4 list-style: none;5 text-decoration: none;6 }7 .page{8 width:500px;9 margin:100px auto; 10 color: #ccc; 11 } 12 .page a{ 13 display: inlin…
Ubuntu 将 /home 或 /var 目录挂载到新的分区
背景 在使用 docker 的过程中,docker 会将某些产物放到 /var/lib/docker/volumes 这会占用很大的跟目录磁盘空间,于是想办法将 /var 目录挂载到另一个一个单独的磁盘上面。 参考链接如下:Ubuntu将var目录挂载到新硬盘 步骤 1. 查看当前磁…
php 500 内部服务器错误,php 500 - 内部服务器错误的解决方法
php 500 - 内部服务器错误的解决方法发布时间:2020-11-04 09:55:31来源:亿速云阅读:71作者:小新小编给大家分享一下php 500 - 内部服务器错误的解决方法,相信大部分人都还不怎么了解,因此分享这篇文章给大家…
网游生命周期在百度指数曲线上呈“M”形分布,各阶段搜索行为呈一定特征
(本文转载自:http://data.baidu.com/youxi3/part1.html) 产品生命周期就是一种新产品从开始进入市场到被市场淘汰的整个过程,进人和退出市场分别标志着周期的开始和结束。网络游戏的生命周期一般可以划分成测试期、成长期、成熟期…
配置Open***使用User/Pass方式验证登录
Open***和PPTP ***相比存在诸多的优势,最明显的是Open***支持NAT穿越,也就是说在nat环境下使用open***只需要一个在路由器上做一个端口映射即可!不需要其他路由的支持,要知道不是所有的路由器都支持配置NAT穿越,只有高…
常惠琢 201771010102《面向对象程序设计(java)》第七周学习总结
实验七 继承附加实验 实验时间 2018-10-11 1、实验目的与要求 (1)进一步理解4个成员访问权限修饰符的用途; (2)掌握Object类的常用API用法; (3)掌握ArrayList类用法与常用API&#…
nginx php站点配置文件,php网站修改默认访问文件的nginx配置
搭建好lnmp后,有时候并不需要直接访问index.php,配置其他的默认访问文件比如index.html这时候需要配置一下nginx才能访问到你想要设置的文件直接上代码,如下是我的配置的一份简单的nginx到php-fpm的站点,该站点默认访问目录/ecmob…
CMake 打包已经存在的动态库生成 target
一. 背景 在 CMakeLists.txt 中,某模块 A 通过 add_subdirectory 引入模块 B ,模块 B 通过 add_subdirectory 引入模块 C。模块 C 里面本身就是一个开源的动态库,比如 libtask。目的想要将 C 模块打包成一个 Target ,以便在 A 模…
Ruby: Ruby脚本在测试中的使用
如果存在以下的场景:1. 放置在公司的读报机服务器,每天都是开启的;2. 读报机每天下载报纸,一旦成功/失败,就会有相应的记录生成在某个xml文件中;3. 现在有过去一年的读报机下载xml,大约有200多份…
cocos2d-xna for win8源代码轻松移植cocos-xna for wp游戏
无意间看到杨哥弄了一个cocos2d-xna for win8出来可惜没有放出源代码,我试着要了一下结果他没理我,各种画圈圈。 那我只好自己弄一个了,源代码放出大家供交流学习使用,像杨哥说的一样就是一点小bug很容易该成把wp改成win8版的&…
Oct 2018
Tasks motion planning code in Autonomous DrivingUdacity RoboticsNDCoursera RoboticsMotion PlanningEstimationF1/10 racecarProject Overview 1. Mooc Udacity RoboticsND: project 3 Coursera Robotics Motion Planning HomeworkNote --> Post 2. Leetcode ☆☆ …
Springboot+mybatisplus搭建新闻管理系统
模块化实现,一周轻松搭建 前端 后端 项目源码及其教程回复已三连备注邮箱领取
SQL Server存储过程输入参数使用表值
在2008之前如果我们想要将表作为输入参数传递给SQL Server存储过程使比较困难的,可能需要很多的逻辑处理将这些表数据作为字符串或者XML传入。 在2008中提供了表值参数。使用表值参数,可以不必创建临时表或许多参数,即可向 Transact-SQL 语句…
ef core mysql 字符集,EF Core 基础知识
数据库连接字符串在 ASP.NET Core 添加配置片段:{"ConnectionStrings": {"BloggingDatabase": "Server(localdb)\\mssqllocaldb;DatabaseEFGetStarted.ConsoleApp.NewDb;Trusted_ConnectionTrue;"}}然后,配置对应的DbCont…
模板特化,全特化,偏特化,全部特化,部分特化的含义
模板特化,任何针对模板参数进一步进行条件限制设计的特化版本。《泛型思维》 全特化就是全部特化,即针对所有的模板参数进行特化。《c primer》偏特化就是部分特化,即针对部分模板参数进行特化。《c primer》全特化和偏特化的定义不是很严格&…
PHP中单例模式:三私一公是什么?
PHP中单例模式:三私一公是什么? 三私一公 私有化静态属性 私有化构造方法 私有化克隆方法 公有化静态方法 转载于:https://www.cnblogs.com/phpisfirst/p/9792545.html
php 缓存模块,PHP缓存之模块缓存(APC)_PHP教程
PHP缓存之模块缓存(APC)APC是Alternative PHP Cache的简称,是 PHP 的一个免费公开的优化代码缓存。它用来提供免费,公开并且强健的架构来缓存和优化 PHP 的中间代码。1、PHP配置APC开启APC模块,需要将 extensionphp_apc.dll 前面的注释去掉即…
pta 考试座位
L1-005 考试座位号 (15 分) 每个 PAT 考生在参加考试时都会被分配两个座位号,一个是试机座位,一个是考试座位。正常情况下,考生在入场时先得到试机座位号码,入座进入试机状态后,系统会显示该考生的考试座位号码&#…
JSDoc那些事
几天工作上需要文档化一些Javascript东西,所以在找一些JS文档化工具,以下分析几种工具。 1.JSDoc-toolkit 一开始还想用这个工具,但后来在解析生成文档时候,出现了很严重的错误,还存在其他问题。 问题1:闭包…
WindowType 属性
WindowType 属性 指定一个表单集或表单在显示或用DO FORM来运行时的行为.在设计和运行时可供使用. Object.WindowType[ nType] 返回值 nType对于表单集,WindowType 属性的设置如下: 设置说明 0 无模式。 1 模式。其它表单(非该表单集中的)不能变为活动…
Excel VBA附合导线平差自动计算表
这是6,7年前做的一个excel vba自动计算附合导线平差的表格。 对于做测绘的朋友来说,附合导线平差是最基础的技能,目前来说,能平差的软件和工具也很多,像南方的平差易,科傻平差、清华三维平差等,但这些软件操…
结构体解决念数字问题
L1-007 念数字 (10 分) 输入一个整数,输出每个数字对应的拼音。当整数为负数时,先输出fu字。十个数字对应的拼音如下: 0: ling 1: yi 2: er 3: san 4: si 5: wu 6: liu 7: qi 8: ba 9: jiu输入格式: 输入在一行中给出一个整数&…