网站首页 > 精选教程 正文
前言
微服务架构下统?认证思路主要有两种形式:
1、基于 Session 的认证?式在分布式的环境下,基于 session 的认证会出现?个问题,每个应?服务都需要在session中存储?户身份信息,通过负载均衡将本地的请求分配到另?个应?服务需要将 session 信息带过去,否则会重新认证。我们可以使? Session 共享、Session 黏贴等?案。Session ?案也有缺点,?如基于 cookie ,移动端不能有效使?等
2、基于 token 的认证?式。基于token的认证?式,服务端不?存储认证数据,易维护扩展性强, 客户端可以把token 存在任意地?,并且可以实现 web 和 app 统?认证机制。其缺点也很明显,token 由于?包含信息,因此?般数据量较?,?且每次请求 都需要传递,因此?较占带宽。另外,token 的签名验签操作也会给 cpu 带来额外的处理负担。
下面我们就基于 token 的认证?式。采用 OAuth2 框架来实现。
OAuth2 开放授权协议/标准
OAuth(开放授权)是?个开放协议/标准,允许?户授权第三?应?访问他们存储在另外的服务提供者上的信息,?不需要将?户名和密码提供给第三?应?或分享他们数据的所有内容。允许?户授权第三?应?访问他们存储在另外的服务提供者上的信息,?不需要将?户名和密码提供给第三?应?或分享他们数据的所有内容。
OAuth2 协议流程图如下:
1、客户端请求用户授权
2、用户确认授权
3、客户端收到授权许可后,向认证服务器申请令牌
4、认证服务器验证授权许可,向客户端返回有效令牌
5、客户端携带有效令牌访问资源服务器
6、资源服务器从认证服务器中验证有效令牌。
7、验证通过后,返回对应的资源给客户端。
什么情况下需要使? OAuth2 ?
第三?授权登录的场景:?如,我们经常登录?些?站或者应?的时候,可以选择使?第三?授权登录的?式,?如:微信授权登录、QQ授权登录、微博授权登录等,这是典型的 OAuth2 使?场景。单点登录的场景:如果项?中有很多微服务或者公司内部有很多服务,可以专?做?个认证中?(充当认证平台??),所有的服务都要到这个认证中?做认证,只做?次登录,就可以在多个授权范围内的服务中?由串?。
Spring Cloud OAuth2 + JWT 实现
Spring Cloud OAuth2 是 Spring Cloud 体系对OAuth2协议的实现,可以?来做多个微服务的统?认证(验证身份合法性)授权(验证权限)。通过向OAuth2服务(统?认证授权服务)发送某个类型的 grant_type 进?集中认证和授权,从?获得 access_token(访问令牌),?这个 token 是受其他微服务信任的。
使? OAuth2 解决问题的本质是,引?了?个认证授权层,认证授权层连接了资源的拥有者,在授权层??,资源的拥有者可以给第三?应?授权去访问我们的某些受保护资源。
搭建认证服务器
创建一个新的的模块,service-oauth-hw-9900。
依赖
pom 文件中依赖如下:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.1.11.RELEASE</version>
</dependency>
<!--引入security对oauth2的支持-->
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
配置文件
server:
port: 9900
spring:
application:
name: service-oauth-hw
zipkin:
base-url: http://127.0.0.1:8771 # zipkin server的请求地址
sender:
# web 客户端将踪迹日志数据通过网络请求的方式传送到服务端,另外还有配置
# kafka/rabbit 客户端将踪迹日志数据传递到mq进行中转
type: web
sleuth:
sampler:
# 采样率 1 代表100%全部采集 ,默认0.1 代表10% 的请求踪迹数据会被采集
# 生产环境下,请求量非常大,没有必要所有请求的踪迹数据都采集分析,对于网络包括server端压力都是比较大的,可以配置采样率采集一定比例的请求的踪迹数据进行分析即可
probability: 1
eureka:
client:
serviceUrl: # eureka server的路径
defaultZone: http://quellanan.a:8761/eureka/,http://quellanan.b:8762/eureka/ #把 eureka 集群中的所有 url 都填写了进来,也可以只写一台,因为各个 eureka server 可以同步注册表
instance:
prefer-ip-address: true #使用ip注册
#分布式链路追踪
logging:
level:
org.springframework.web.servlet.DispatcherServlet: debug
org.springframework.cloud.sleuth: debug
启动类
@SpringBootApplication
@EnableDiscoveryClient
public class ServiceOauthHw9900Application {
public static void main(String[] args) {
SpringApplication.run(ServiceOauthHw9900Application.class, args);
}
}
config
自定义一个 OauthServerConfiger。当前类为Oauth2 server的配置类(需要继承特定的父类 AuthorizationServerConfigurerAdapter)
@Configuration
@EnableAuthorizationServer //开启认证服务器功能
public class OauthServerConfiger extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
/**
* 客户端详情配置,
* 比如client_id,secret
* 当前这个服务就如同QQ平台,拉勾网作为客户端需要qq平台进行登录授权认证等,提前需要到QQ平台注册,QQ平台会给拉勾网
* 颁发client_id等必要参数,表明客户端是谁
* @param clients
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
super.configure(clients);
// 客户端信息存储在什么地方,可以在内存中,可以在数据库里
clients.inMemory()
// 添加一个client配置,指定其client_id
.withClient("quellanan")
//指定客户端的密码/安全码
.secret("abcdefg")
//指定客户端所能访问资源id清单,此处的资源id是需要在具体的资源服务器上也配置一样
.redirectUris("*")
//认证类型/令牌颁发模式,可以配置多个在这里,但是不一定都用,具体使用哪种方式颁发token,需要客户端调用的时候传递参数指定
.authorizedGrantTypes("password","refresh_token")
//客户端的权限范围,此处配置为all全部即可
.scopes("all");
}
/**
* 认证服务器最终是以api接口的方式对外提供服务(校验合法性并生成令牌、校验令牌等)
* 那么,以api接口方式对外的话,就涉及到接口的访问权限,我们需要在这里进行必要的配置
* @param security
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
super.configure(security);
// 相当于打开endpoints 访问接口的开关,这样的话后期我们能够访问该接口
security
// 允许客户端表单认证
.allowFormAuthenticationForClients()
// 开启端口/oauth/token_key的访问权限(允许)
.tokenKeyAccess("permitAll()")
// 开启端口/oauth/check_token的访问权限(允许)
.checkTokenAccess("permitAll()");
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
super.configure(endpoints);
endpoints
// 指定token的存储方法
.tokenStore(tokenStore())
// token服务的一个描述,可以认为是token生成细节的描述,比如有效时间多少等
.tokenServices(authorizationServerTokenServices())
// 指定认证管理器,随后注入一个到当前类使用即可
.authenticationManager(authenticationManager)
.allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST);
}
/*
该方法用于创建tokenStore对象(令牌存储对象)
token以什么形式存储
*/
public TokenStore tokenStore(){
return new InMemoryTokenStore();
// 使用jwt令牌
//return new JwtTokenStore(jwtAccessTokenConverter());
}
/**
* 该方法用户获取一个token服务对象(该对象描述了token有效期等信息)
*/
public AuthorizationServerTokenServices authorizationServerTokenServices() {
// 使用默认实现
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setSupportRefreshToken(true); // 是否开启令牌刷新
defaultTokenServices.setTokenStore(tokenStore());
// 针对jwt令牌的添加
//defaultTokenServices.setTokenEnhancer(jwtAccessTokenConverter());
// 设置令牌有效时间(一般设置为2个小时)
defaultTokenServices.setAccessTokenValiditySeconds(20); // access_token就是我们请求资源需要携带的令牌
// 设置刷新令牌的有效时间
defaultTokenServices.setRefreshTokenValiditySeconds(259200); // 3天
return defaultTokenServices;
}
}
关于三个 configure ?法
configure(ClientDetailsServiceConfifigurer clients):?来配置客户端详情服务(ClientDetailsService),客户端详情信息在 这?进?初始化,你能够把客户端详情信息写死在这?或者是通过数据库来存储调取详情信息
confifigure(AuthorizationServerEndpointsConfifigurer endpoints):?来配置令牌(token)的访问端点和令牌服务(token services)
confifigure(AuthorizationServerSecurityConfifigurer oauthServer):?来配置令牌端点的安全约束.
关于 TokenStore
InMemoryTokenStore默认采?,它可以完美的?作在单服务器上(即访问并发量 压?不?的情况下,并且它在失败的时候不会进?备份),?多数的项?都可以使?这个版本的实现来进? 尝试,你可以在开发的时候使?它来进?管理,因为不会被保存到磁盘中,所以更易于调试。
JdbcTokenStore这是?个基于JDBC的实现版本,令牌会被保存进关系型数据库。使?这个版本的实现时, 你可以在不同的服务器之间共享令牌信息,使?这个版本的时候请注意把"springjdbc"这个依赖加?到你的 classpath当中。JwtTokenStore 这个版本的全称是 JSON Web Token(JWT),它可以把令牌相关的数据进?编码(因此对于后端服务来说,它不需要进?存储,这将是?个重?优势),缺点就是这个令牌占?的空间会?较?,如果你加?了?较多?户凭证信息,JwtTokenStore 不会保存任何数据。
然后再自定义有一个配置类,主要处理用户名和密码的校验等事宜。
@Configuration
public class SecurityConfiger extends WebSecurityConfigurerAdapter {
@Autowired
private PasswordEncoder passwordEncoder;
//@Autowired
//private JdbcUserDetailsService jdbcUserDetailsService;
/**
* 注册一个认证管理器对象到容器
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
/**
* 密码编码对象(密码不进行加密处理)
* @return
*/
@Bean
public PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
/**
* 处理用户名和密码验证事宜
* 1)客户端传递username和password参数到认证服务器
* 2)一般来说,username和password会存储在数据库中的用户表中
* 3)根据用户表中数据,验证当前传递过来的用户信息的合法性
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 在这个方法中就可以去关联数据库了,当前我们先把用户信息配置在内存中
// 实例化一个用户对象(相当于数据表中的一条用户记录)
UserDetails user = new User("admin","123456",new ArrayList<>());
auth.inMemoryAuthentication()
.withUser(user).passwordEncoder(passwordEncoder);
//auth.userDetailsService(jdbcUserDetailsService).passwordEncoder(passwordEncoder);
}
}
JWT 改造统?认证授权中?的令牌存储机制
JWT 令牌介绍
通过上边的测试我们发现,当资源服务和授权服务不在?起时资源服务使?RemoteTokenServices 远程请求授权 服务验证token,如果访问量较?将会影响系统的性能。
解决上边问题: 令牌采?JWT格式即可解决上边的问题,?户认证通过会得到?个JWT令牌,JWT令牌中已经包括了?户相关的信 息,客户端只需要携带JWT访问资源服务,资源服务根据事先约定的算法??完成令牌校验,?需每次都请求认证 服务完成授权。
什么是JWT?
JSON Web Token(JWT)是?个开放的?业标准(RFC 7519),它定义了?种简介的、?包含的协议格式,?于 在通信双?传递json对象,传递的信息经过数字签名可以被验证和信任。JWT可以使?HMAC算法或使?RSA的公 钥/私钥对来签名,防?被篡改。
JWT令牌结构
JWT 令牌由三部分组成,每部分中间使?点(.)分隔,?如:xxxxx.yyyyy.zzzzz
Header。头部包括令牌的类型(即JWT)及使?的哈希算法(如HMAC SHA256或RSA),例如
{
"alg": "HS256",
"typ": "JWT"
}
将上边的内容使?Base64Url编码,得到?个字符串就是JWT令牌的第?部分。
Payload。第?部分是负载,内容也是?个json对象,它是存放有效信息的地?,它可以存放jwt提供的现成字段,? 如:iss(签发者),exp(过期时间戳), sub(?向的?户)等,也可?定义字段。 此部分不建议存放敏感信息,因为此部分可以解码还原原始内容。 最后将第?部分负载使?Base64Url编码,得到?个字符串就是JWT令牌的第?部分。 ?个例?:
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
Signature。第三部分是签名,此部分?于防?jwt内容被篡改。 这个部分使?base64url将前两部分进?编码,编码后使?点(.)连接组成字符串,最后使?header中声明 签名算法进?签名。
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
- base64UrlEncode(header):jwt令牌的第?部分。
- base64UrlEncode(payload):jwt令牌的第?部分。
secret:签名所使?的密钥。
认证服务器端JWT改造(改造主配置类)
/*
该方法用于创建tokenStore对象(令牌存储对象)
token以什么形式存储
*/
public TokenStore tokenStore(){
//return new InMemoryTokenStore();
// 使用jwt令牌
return new JwtTokenStore(jwtAccessTokenConverter());
}
/**
* 返回jwt令牌转换器(帮助我们生成jwt令牌的)
* 在这里,我们可以把签名密钥传递进去给转换器对象
* @return
*/
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
jwtAccessTokenConverter.setSigningKey(sign_key); // 签名密钥
jwtAccessTokenConverter.setVerifier(new MacSigner(sign_key)); // 验证时使用的密钥,和签名密钥保持一致
jwtAccessTokenConverter.setAccessTokenConverter(lagouAccessTokenConvertor);
return jwtAccessTokenConverter;
}
修改 JWT 令牌服务?法
/**
* 该方法用户获取一个token服务对象(该对象描述了token有效期等信息)
*/
public AuthorizationServerTokenServices authorizationServerTokenServices() {
// 使用默认实现
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setSupportRefreshToken(true); // 是否开启令牌刷新
defaultTokenServices.setTokenStore(tokenStore());
// 针对jwt令牌的添加
defaultTokenServices.setTokenEnhancer(jwtAccessTokenConverter());
// 设置令牌有效时间(一般设置为2个小时)
defaultTokenServices.setAccessTokenValiditySeconds(20); // access_token就是我们请求资源需要携带的令牌
// 设置刷新令牌的有效时间
defaultTokenServices.setRefreshTokenValiditySeconds(259200); // 3天
return defaultTokenServices;
}
总结
我们在实际工作中,token 鉴权的方式是很常见的现在,这一套解决方案也可以直接使用到项目中,小伙伴们赶紧学习起来吧。
猜你喜欢
- 2024-11-11 RuoYi若依系统的验证码如何替换为更美观的EasyCaptcha
- 2024-11-11 「Java」伪共享验证 什么是伪共享?伪共享会导致运算结果错误吗?
- 2024-11-11 Azure上的Java:云原生身份验证 azure java
- 2024-11-11 Java之HTTP请求权限验证 java之http请求权限验证怎么办
- 2024-11-11 java短信验证平台_JAVA实现利用第三方平台发送短信验证码
- 2024-11-11 Java 的业务逻辑验证框架 之-fluent-validator
- 2024-11-11 java 生成4位短信验证码方法 java+生成4位短信验证码方法有哪些
- 2024-11-11 Java策略模式实现动态验证不同来源的数据
- 2024-11-11 Java安全编程:公钥加密和私钥签名的实践指南
- 2024-11-11 Python和Java生成动态随机验证码 生成随机验证码,python
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- nginx反向代理 (57)
- nginx日志 (56)
- nginx限制ip访问 (62)
- mac安装nginx (55)
- java和mysql (59)
- java中final (62)
- win10安装java (72)
- java启动参数 (64)
- java链表反转 (64)
- 字符串反转java (72)
- java逻辑运算符 (59)
- java 请求url (65)
- java信号量 (57)
- java定义枚举 (59)
- java字符串压缩 (56)
- java中的反射 (59)
- java 三维数组 (55)
- java插入排序 (68)
- java线程的状态 (62)
- java异步调用 (55)
- java中的异常处理 (62)
- java锁机制 (54)
- java静态内部类 (55)
- java怎么添加图片 (60)
- java 权限框架 (55)
本文暂时没有评论,来添加一个吧(●'◡'●)