网站后台无法访问,网站开发辅助工具,oa软件公司排名,wordpress 执行php最近的项目学习中#xff0c;在进行登陆模块的用户信息验证这一部分又用到了JWT的一些概念和相关知识#xff0c;特在此写了这篇文章、方便各位笔者理解JWT相关概念 目录
先来理解JWT是什么#xff1f;
区分有状态认证和无状态认证
有状态认证 VS 无状态认证
JWT令牌的…最近的项目学习中在进行登陆模块的用户信息验证这一部分又用到了JWT的一些概念和相关知识特在此写了这篇文章、方便各位笔者理解JWT相关概念 目录
先来理解JWT是什么
区分有状态认证和无状态认证
有状态认证 VS 无状态认证
JWT令牌的优点
JWT令牌的三个部分
测试生成JWT令牌
实例携带令牌访问资源服务
网关 — 完善架构 先来理解JWT是什么 JWT全称为JSON Web Token是一种开放标准RFC 7519主要用于在网络应用间安全地传递信息是一种基于JSON的紧凑独立的令牌格式通常用于身份验证和信息交换 它的主要使用场景
用于身份验证用于登录和后续请求的验证
举一个现实中的例子假设我们正在使用淘宝平台登录流程大致如下 1.当我们输入了用户名和密码点击”登录“网站服务器验证你的凭证 2.验证成功后服务器生成了一个JWT返回给你的浏览器 3.如果继续浏览商品或下单时浏览器会在请求中携带这个JWT网站服务器通过验证JWT确认你的身份 4.确认身份后就能访问购物车下单或查看订单详情 在分布式系统或多服务架构中JWT负责在不同的服务之间传递用户信息 假设有一个电商平台其中包括两个服务服务A用户服务和服务B订单服务用户通过服务A登陆后需要将用户信息传递给服务B完成订单操作以便服务B能够根据用户身份处理订单相关的操作 区分有状态认证和无状态认证
有状态认证介绍
传统的基于session的方式是有状态认证用户登录成功将用户的身份信息存储在服务端这样会加大服务端的存储压力这种方式也不适合在分布式系统中应用
如下图当用户访问应用服务每个应用服务都会去服务器查看session信息如果session中没有该用户则说明用户没有登录此时就会重新认证而解决这个问题的方法就是Session复制Session黏贴 无状态认证
如果是基于令牌技术在分布式系统中实现认证则服务端不用存储session可以将用户身份信息存储在令牌中用户认证通过后认证服务颁发令牌给用户用户将令牌存储在客户端去访问应用服务时携带令牌去访问服务端从JWT解析出用户信息这个过程就是无状态认证 有状态认证 VS 无状态认证
有状态流程 用户登录 — 服务端创建Session并存储用户信息 — 返回Session ID给客户端 — 客户端携带 Session ID访问应用服务 — 服务端根据Session ID查找用户信息 — 返回响应 优点
1.安全性较高用户的信息存储在服务端客户端仅持有Session ID不容易泄露敏感数据
2.易于控制服务端可以随时使Session失效比如用户注销超时
缺点
1.服务端存储压力大每个用户的Session信息都需要存储在服务端用户量较大时对内存和存储资源要求高
2.不适合分布系统在分布式系统中Session需要共享比如通过Redis集群复杂性增加新增服务器时 还需要同步Session数据
无状态认证流程 用户登录 — 服务端生成Token令牌并返回给客户端 — 客户端携带Token令牌访问应用服务 — 服务端解析Token令牌获取用户信息 —返回响应 优点
1.无需存储信息服务端无需存储用户信息适合分布式系统和微服务架构
2.无需同步新增服务器时无需同步数据降低了运维成本
3.实现跨域共享令牌可以轻松实现跨域资源共享
缺点
1.令牌长度较大比Session ID长可能会增加网络开销
2.Token在过期前始终有效无法像Session一样主动注销
3.JWT的Payload是Base64编码的可以被解码不适合存储敏感信息
分析这两个有无状态认证总结出以下几点
有状态认证更适合传统Web应用像银行系统这样需要严格会话控制的场景
无状态认证更适合分布式系统微服务架构移动端和前后端分离的应用
JWT令牌的优点
1.JWT基于JSON易于解析
JWT的Header和Payload是Base64编码的解码后可以直接解析为JSON对象开发者可以轻松地从JWT中提取所需的信息比如用户ID角色..
{userId: 123,username: john_doe,role: admin
}
2.可以在令牌中自定义丰富的内容
JWT的Payload部分可以包含任意自定义的声明claims可以在Payload中添加用户角色权限过期时间等信息如果要添加新的信息如添加用户邮箱地址等只需在Payload中添加新的字段无需修改现有逻辑
{userId: 123,username: john_doe,role: admin,//新增了邮箱 电话等信息email: johnexample.com,exp: 1698765432
}
3.通过非对称加密算法以及数字签名技术防止篡改安全性高
JWT的Signature部分是通过Header和Payload使用指定的算法如HMAC SHA256或RSA SHA256生成的
如果攻击者修改了JWT的Header或Payload签名将不再匹配验证时会失败
同样的缺点如下JWT令牌较长占的内存存储空间较大 JWT令牌的示例
JWT令牌的三个部分
以上这段JWT令牌代码包含了三部分用点号.分隔
Header头部包括令牌的类型既JWT以及使用的哈希算法HMAC SHA256或RSA
Base64编码前的JSON
{alg: HS256, //alg:签名算法这里是HS256typ: JWT //令牌类型这里是JWT
}
Base64编码后的Header以上令牌示例图第一部分
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Payload第二部分是负载内容也是一个json对象它是存放有效信息的地方可以存放jwt提供的信息字段比如iss签发者exp过期时间戳sub面向的用户等也可以自定义字段
Base64编码前的Payload
{aud: [res1], //令牌的目标受众这里是[rset]user_name: zhangsan,//用户名scope: [all], //权限范围exp: 1664254672, //令牌的过期时间(Unix时间戳)authorities: [p1], //用户的权限这里是[p1]jti: 88912b2d-5d05-4c14-bbc3-fde9977febc6,//令牌的唯一标识符client_id: c1 //客户端ID这里是c1
}
Base64编码后的Payload以上令牌示例图第二部分
eyJhdWQiOlsicmVzMSJdLCJ1c2VyX25hbWUiOiJ6aGFuZ3NhbiIsInNjb3BlIjpbImFsbCJdLCJleHAiOjE2NjQyNTQ2NzIsImF1dGhvcml0aWVzIjpbInAxIl0sImp0aSI6Ijg4OTEyYjJk
LTVkMDUtNGMxNC1iYmMzLWZkZTk5NzdmZWJjNiIsImNsaWVudF9pZCI6ImMxIn0
Signature签名签名是通过对Header和Payload进行加密生成的将Header和Payload分别进行Base64Url编码
编码后将HeaderPayload和Signature用点号.拼接起来形成完整的JWT
//拼接Header payload 对Header和Payload的签名验证令牌是否被篡改
HMACSHA256(base64UrlEncode(header) . base64UrlEncode(payload),secret) //secret:签名所使用的密钥
以上令牌示例图最后一部分就是编码后的Signature
wkDBL7roLrvdBG2oGnXeoXq-zZRgE9IVV2nxd-ez_oA
为什么JWT可以防止篡改
第三部分使用签名算法对Header和Payload的内容进行签名常见的签名算法是HS256常见的还有MD5,SHA等...签名算法需要使用密钥进行签名密钥不对外公开并且签名是不可逆的如果第三方更改了内容那么服务器验证签名就会失败要想保证验证签名正确必须保证内容密钥与签名前一致 从上图中可以看出认证服务和资源服务使用了相同的密钥这叫做对称加密对称加密效率高如果一旦密钥泄露可以伪造JWT令牌
JWT还可以使用非对称加密认证服务自己保留私钥将公钥下发给受信任的客户端资源服务公钥和私钥是配对的成对的公钥和私钥才可以正常加密和解密非对称加密效率低但相比对称加密非对称加密更安全一些
测试生成JWT令牌
在认证服务中配置JWT令牌服务即可实现生成JWT格式的令牌
package com.xuecheng.auth.config;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;import java.util.Arrays;/*** author Administrator* version 1.0**/
Configuration
public class TokenConfig {private String SIGNING_KEY mq123;AutowiredTokenStore tokenStore;// Bean
// public TokenStore tokenStore() {
// //使用内存存储令牌普通令牌
// return new InMemoryTokenStore();
// }Autowiredprivate JwtAccessTokenConverter accessTokenConverter;Beanpublic TokenStore tokenStore() {return new JwtTokenStore(accessTokenConverter());//使用JwtTokenStore替代默认的InMemoryTokenStore表示令牌以JWT格式存储}//配置JWT的签名密钥和转换逻辑Beanpublic JwtAccessTokenConverter accessTokenConverter() {JwtAccessTokenConverter converter new JwtAccessTokenConverter();converter.setSigningKey(SIGNING_KEY);return converter;}//令牌管理服务 定义令牌服务的核心行为如令牌生成刷新有效期等Bean(nameauthorizationServerTokenServicesCustom)public AuthorizationServerTokenServices tokenService() {DefaultTokenServices servicenew DefaultTokenServices();service.setSupportRefreshToken(true);//支持刷新令牌service.setTokenStore(tokenStore);//令牌存储策略TokenEnhancerChain tokenEnhancerChain new TokenEnhancerChain();tokenEnhancerChain.setTokenEnhancers(Arrays.asList(accessTokenConverter));service.setTokenEnhancer(tokenEnhancerChain);service.setAccessTokenValiditySeconds(7200); // 令牌默认有效期2小时service.setRefreshTokenValiditySeconds(259200); // 刷新令牌默认有效期3天return service;}
}
重启认证服务
使用httpclient通过密码模式申请令牌
### 密码模式
POST {{auth_host}}/oauth/token?client_idXcWebAppclient_secret
XcWebAppgrant_typepasswordusernamezhangsanpassword123
生成的JWT示例如下
{access_token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsicmVzMSJdLCJ1c2VyX25hbWUiOi
J6aGFuZ3NhbiIsInNjb3BlIjpbImFsbCJdLCJleHAiOjE2NjQzMzE2OTUsImF1dGhvcml0aWVzIj
pbInAxIl0sImp0aSI6ImU5ZDNkMGZkLTI0Y2ItNDRjOC04YzEwLTI1NmIzNGY4ZGZjYyIsImNsaW
VudF9pZCI6ImMxIn0.-9SKI-qUqKhKcs8Gb80Rascx-JxqsNZxxXoPo82d8SM, //生成的JWT令牌token_type: bearer,refresh_token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsicmVzMSJdLCJ1c2VyX25hbWUiOiJ
6aGFuZ3NhbiIsInNjb3BlIjpbImFsbCJdLCJhdGkiOiJlOWQzZDBmZC0yNGNiLTQ0YzgtOGMxMC0y
NTZiMzRmOGRmY2MiLCJleHAiOjE2NjQ1ODM2OTUsImF1dGhvcml0aWVzIjpbInAxIl0sImp0aSI6I
mRjNTRjNTRkLTA0YTMtNDIzNS04MmY3LTFkOWZkMmFjM2VmNSIsImNsaWVudF9pZCI6ImMxIn0.Ws
w1Jc-Kd_GFqEugzdfoSsMY6inC8OQsraA21WjWtT8,expires_in: 7199,scope: all,jti: e9d3d0fd-24cb-44c8-8c10-256b34f8dfcc
}
1.access-token部分这部分是生成的JWT令牌用于访问资源使用
2.token_type: 这部分的bearer是在REC6750中定义的一种token类型在携带JWT访问资源时需要在head中加入bearer jwt令牌内容
###校验jwt令牌
POST {{auth_host}}/oauth/check_token?tokeneyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsicmVzMSJdLCJ1c2VyX25hbWUiOiJzdHUxIiwic2NvcGUiOlsiYWxsIl0sImV4cCI6MTY2NDM3MTc4MCwiYXV0aG9yaXRpZXMiOlsicDEiXSwianRpIjoiZjBhM2NkZWItMzk5ZC00OGYwLTg4MDQtZWNhNjM4YWQ4ODU3IiwiY2xpZW50X2lkIjoiYzEifQ.qy46CSCJsH3eXWTHgdcntZhzcSzfRQlBU0dxAjZcsUw
3.refresh_token当JWT令牌快过期时使用刷新令牌可以再次生成JWT令牌
4.expires_in过期时间秒
5.scope令牌的权限范围服务端可以根据令牌的权限范围去对令牌授权
6.jti令牌的唯一标识
我们可以通过check_token接口校验JWT令牌
###校验jwt令牌
POST {{auth_host}}/oauth/check_token?tokeneyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsicmVzMSJ
dLCJ1c2VyX25hbWUiOiJzdHUxIiwic2NvcGUiOlsiYWxsIl0sImV4cCI6MTY2
NDM3MTc4MCwiYXV0aG9yaXRpZXMiOlsicDEiXSwianRpIjoiZjBhM2NkZWItM
zk5ZC00OGYwLTg4MDQtZWNhNjM4YWQ4ODU3IiwiY2xpZW50X2lkIjoiYzEifQ.
qy46CSCJsH3eXWTHgdcntZhzcSzfRQlBU0dxAjZcsUw
响应如下 {aud: [res1],user_name: zhangsan,scope: [all],active: true,exp: 1664371780,authorities: [p1],jti: f0a3cdeb-399d-48f0-8804-eca638ad8857,client_id: c1
}
实例携带令牌访问资源服务
拿到了JWT令牌下一步就要携带令牌去访问资源服务中的资源比如在线教育项目内容管理服务模块客户端申请到JWT令牌携带JWT去内容管理服务查询课程信息此时内容管理服务要对JWT进行校验只有JWT合法才可以继续访问如图所示流程 案例演示在内存管理服务在线教育系统中配置OAuth2资源服务并测试携带JWT令牌访问受保护接口比较有无JWT令牌会产生什么结果
1.在内容管理服务的content-api工程中添加依赖
!--引入Spring Security和OAuth2支持使服务能验证JWT令牌并保护API端点--
dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-security/artifactId !--依赖1--
/dependency
dependencygroupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-oauth2/artifactId !--依赖2--
/dependency
加入的依赖用途
依赖1:继承基本安全功能认证授权
依赖2集成OAuth2协议支持资源服务器配置
2.在内容管理服务的content-api中添加TokenConfig配置类
目的用于配置JWT相关组件 — JWT的签名密钥和存储方式
Configuration
public class TokenConfig {private String SIGNING_KEY mq123;Beanpublic JwtAccessTokenConverter accessTokenConverter(){JwtAccessTokenConverter converter new JwtAccessTokenConverter();converter.setSigningKey(SIGNING_KEY);return converter;}Beanpublic TokenStore tokenStore() {return new JwtTokenStore(accessTokenConverter());}
}
3.添加资源服务配置类ResourceServerConfig
目的配置资源服务器的安全规则通过EnableResourceServer启动资源服务器的功能
Configuration
EnableResourceServer
EnableGlobalMethodSecurity(securedEnabled true,prePostEnabled true)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {public static final String RESOURCE_ID xuecheng-plus;AutowiredTokenStore tokenStore;Overridepublic void configure(ResourceServerSecurityConfigurer resources) {resources.resourceId(RESOURCE_ID).tokenStore(tokenStore).stateless(true);}Overridepublic void configure(HttpSecurity http) throws Exception {http.csrf().disable() // 禁用 CSRF 保护.authorizeRequests() //配置对请求的授权策略.antMatchers(/r/**, /course/**).authenticated() // 指定 /r/ 和 /course/ 这两个路径需要进行身份认证才能访问。.anyRequest().permitAll(); // 允许所有其他请求除了上面指定的路径之外都可以被访问不需要进行身份认证。}
}
其中.antMatchers(/r/**, /course/**).authenticated() 这些路径请求必须携带有效令牌否则返回401 Unauthorized
重启内容管理服务使用httpclient测试
1.访问根据课程id查询课程接口
### 查询课程信息
GET http://localhost:63040/content/course/2
返回结果 {error: unauthorized,error_description: Full authentication is required to access this resource
}
从返回信息可知当前没有认证测试未在请求体提供有效的JWT令牌资源服务器拦截了请求
下边携带JWT令牌访问接口
1.申请JWT令牌采用密码模式申请令牌
###### 密码模式
POST {{auth_host}}/auth/oauth/token?client_idMsWebAppclient_secretMsWebAppgrant_typepasswordusernameKylepassword123
2.携带JWT令牌访问资源服务地址
### 携带token访问资源服务
GET http://localhost:63040/content/course/2
Authorization: Bearer
//JWT令牌
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsicmVzMSJdLCJ1c2VyX25hbWUiOiJ6a
GFuZ3NhbiIsInNjb3BlIjpbImFsbCJdLCJleHAiOjE2NjQzMzM0OTgsImF1dGhvcml0aWVzIjpbInA
xIl0sImp0aSI6IjhhM2M2OTk1LWU1ZGEtNDQ1Yy05ZDAyLTEwNDFlYzk3NTkwOSIsImN
saWVudF9pZCI6ImMxIn0.73eNDxTX5ifttGCjwc7xrd-Sbp_mCfcIerI3lGetZto
3.如果携带JWT令牌且JWT令牌正确则正常访问资源服务的内容
{
id: 129,
companyId: 12293202020,
companyName: null,
name: 臭臭,
users: 君子 ,
tags: ,
mt: 1-5,
st: 1-5-4,
grade: 204003,
teachmode: 200002,
description: null,
pic: /mediafiles/2023/03/03/76ac562669dc346992af9dd039060e7b.jpg,
createDate: 2025-02-25 17:17:07,
changeDate: 2025-02-26 11:09:31,
createPeople: null,
changePeople: null,
auditStatus: 203002,
status: 203001,
charge: 201000,
price: 0.0,
originalPrice: null,
qq: ,
wechat: ,
phone: ,
validDays: 365,
mtName: 人工智能,
stName: 计算机科学
}
如果不正确则报令牌无效的错误例如
{error: invalid_token,error_description: Cannot convert access token to JSON
}
测试获取用户身份
JWT令牌中同样也记录了用户身份信息当客户端携带JWT访问资源服务资源服务验证签名通过后将前两部分的内容还原即可取出用户的身份信息并将用户身份信息放在了SecurityContextHolder上下文SecurityContext与当前线程进行绑定方便获取用户身份
以在线教育系统查看课程接口为例进入查询课程接口的代码中添加获取用户身份的代码
ApiOperation(根据课程id查询课程基础信息)
GetMapping(/course/{courseId})
public CourseBaseInfoDto getCourseBaseById(PathVariable(courseId) Long courseId){//取出当前用户身份Object principal SecurityContextHolder.getContext().getAuthentication().getPrincipal();System.out.println(当前用户身份为principal);return courseBaseInfoService.getCourseBaseInfo(courseId);
}
测试时需要注意 1.首先在资源服务配置中指定安全拦截机制/course/开头的请求需要认证既请求/course/{courseId}接口需要携带JWT令牌且签证通过
2.认证服务生成JWT令牌将用户身份信息写入令牌目前还是将用户信息硬编码并暂放到内存中
如下
Bean
public UserDetailsService userDetailsService() {//这里配置用户信息,这里暂时使用这种方式将用户存储在内存中InMemoryUserDetailsManager manager new InMemoryUserDetailsManager();manager.createUser(User.withUsername(liwenwen).password(123).authorities(p1).build());manager.createUser(User.withUsername(yiyangyang).password(456).authorities(p2).build());return manager;
}
3.我们在使用密码模式生成JWT令牌时用的是lliwenwen的信息所以JWT令牌中存储了liwenwen的信息那么在资源服务中应该取出liwenwen的信息才对
了解了这些内容使用HttpClient测试接口重启内容管理服务跟踪取到的用户身份是正确的结果如下
当前用户身份为liwenwen
至此用户登录通过了认证服务颁发了JWT令牌客户端携带JWT访问资源服务资源服务对JWT的合法性进行验证如下图 网关 — 完善架构
但是这样的操作流程似乎遗漏了架构中非常重要的组件网关加上之后并完善后如下图所示 注所有访问微服务的请求都要经过网关在网关进行用户身份的认证可以将很多非法的请求拦截到微服务以外这叫做网关认证
网关的职责
1.网络白名单维护针对不用认证的URL全部放行
2.校验JWT的合法性除了白名单剩下的就是需要认证的请求网关需要验证JWT的合法性JWT合法则说明用户身份合法否则说明身份不合法则拒绝继续访问
网关负责授权码
答网关不负责授权对请求的授权操作在各个微服务进行因为微服务最清楚用户有哪些权限访问哪些接口
下面实现网关认证
1.在网关工程添加依赖
dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-security/artifactId
/dependency
dependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-starter-oauth2/artifactId
/dependency
dependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactId
/dependency
dependencygroupIdcom.alibaba/groupIdartifactIdfastjson/artifactId
/dependency
2.将网关鉴权配置类添加在项目的config包下
GatewayAuthFilter类
Component
Slf4j
public class GatewayAuthFilter implements GlobalFilter, Ordered {//白名单private static ListString whitelist null;static {//加载白名单 白名单中的路径无需认证即可访问如登录接口静态资源try (InputStream resourceAsStream GatewayAuthFilter.class.getResourceAsStream(/security-whitelist.properties);) {Properties properties new Properties();properties.load(resourceAsStream);SetString strings properties.stringPropertyNames();whitelist new ArrayList(strings);} catch (Exception e) {log.error(加载/security-whitelist.properties出错:{}, e.getMessage());e.printStackTrace();}}Autowiredprivate TokenStore tokenStore;Overridepublic MonoVoid filter(ServerWebExchange exchange, GatewayFilterChain chain) {String requestUrl exchange.getRequest().getPath().value();AntPathMatcher pathMatcher new AntPathMatcher();//白名单放行for (String url : whitelist) {if (pathMatcher.match(url, requestUrl)) {return chain.filter(exchange);}}//检查token是否存在String token getToken(exchange);if (StringUtils.isBlank(token)) {return buildReturnMono(没有认证, exchange);}//判断是否是有效的tokenOAuth2AccessToken oAuth2AccessToken;try {oAuth2AccessToken tokenStore.readAccessToken(token);boolean expired oAuth2AccessToken.isExpired();if (expired) {return buildReturnMono(认证令牌已过期, exchange);}return chain.filter(exchange);} catch (InvalidTokenException e) {log.info(认证令牌无效: {}, token);return buildReturnMono(认证令牌无效, exchange);}}/*** 获取token*/private String getToken(ServerWebExchange exchange) {String tokenStr exchange.getRequest().getHeaders().getFirst(Authorization);if (StringUtils.isBlank(tokenStr)) {return null;}String token tokenStr.split( )[1];if (StringUtils.isBlank(token)) {return null;}return token;}private MonoVoid buildReturnMono(String error, ServerWebExchange exchange) {ServerHttpResponse response exchange.getResponse();String jsonString JSON.toJSONString(new RestErrorResponse(error));byte[] bits jsonString.getBytes(StandardCharsets.UTF_8);DataBuffer buffer response.bufferFactory().wrap(bits);response.setStatusCode(HttpStatus.UNAUTHORIZED);response.getHeaders().add(Content-Type, application/json;charsetUTF-8);return response.writeWith(Mono.just(buffer));}Overridepublic int getOrder() {return 0;}
}该过滤器用于拦截所有经过网关的请求检查请求是否在白名单中或者是否携带有效的JWT令牌 1.如果请求在白名单中直接放行 2.如果请求不在白名单中检查是否携带有效的JWT令牌 令牌有效 ----- 放行 令牌无效或过期 ----- 返回401 Unauthorized错误 RestErrorResponse类
用于封装错误响应信息在RESTful API中返回统一的错误格式
public class RestErrorResponse implements Serializable {private String errMessage;public RestErrorResponse(String errMessage){this.errMessage errMessage;}public String getErrMessage() {return errMessage;}public void setErrMessage(String errMessage) {this.errMessage errMessage;}
}
SecurityConfig类
通过EnableWebFluxSecurity注解启动了Spring WebFlux的安全功能并定义一个SecurityWebFilterChain Bean用于配置请求的安全拦截规则
EnableWebFluxSecurity
Configuration
public class SecurityConfig {//安全拦截配置Beanpublic SecurityWebFilterChain webFluxSecurityFilterChain(ServerHttpSecurity http) {return http.authorizeExchange().pathMatchers(/**).permitAll().anyExchange().authenticated().and().csrf().disable().build();}
}
TokenConfig类
该Spring配置类用于配置JWT相关的组件包括TokenStore和JwtAccessTokenConverter
Configuration
public class TokenConfig {String SIGNING_KEY mq123;//定义了TokenStore Bean用来存储和管理JWT令牌Beanpublic TokenStore tokenStore() {return new JwtTokenStore(accessTokenConverter());}//用于将OAuth2访问令牌与JWT进行转换Beanpublic JwtAccessTokenConverter accessTokenConverter() {JwtAccessTokenConverter converter new JwtAccessTokenConverter();converter.setSigningKey(SIGNING_KEY);return converter;}
}
配置白名单文件security-whitelist.properties
/auth/**认证地址
/content/open/**内容管理公开访问接口
/media/open/**媒资管理公开访问接口
重启网关工程进行测试
1.申请令牌
2.通过网关访问资源服务
这里访问内容管理服务
### 通过网关访问资源服务
GET http://localhost:63010/content/course/2
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsicmVzMSJdLCJ1c2VyX
25hbWUiOiJ6aGFuZ3NhbiIsInNjb3BlIjpbImFsbCJdLCJleHAiOjE2NjQzNjIzMTAsImF1dGhvcml0aWVzIjpbInAxIl0sImp0aSI6Ijc2OTkwMGNiLWM1ZjItNGRiNC1hZWJmLWY1MzgxZDQxZWMyZCIsImNsaWVudF9pZCI6ImMxIn0.lOITjUgYg2HCh5mDPK9EvJJqz-tIupKVfmP8yWJQIKs
当token正确时就可以正常访问资源服务token验证失败返回token无效
{errMessage: 认证令牌无效
}
至此了解了使用Spring Security进行认证授权的过程本篇只对Spring Security做了一个简单的介绍要掌握并结合各种认证方式实现系统的登录认证模块开发还需要参考一些实例