分布式系统认证方案

五、分布式系统认证方案

5.1 什么是分布式系统

随着软件环境和需求的变化,软件的架构由单体结构演变为分布式架构,具有分布式架构的系统叫分布式系统,分 布式系统的运行通常依赖网络,它将单体结构的系统分为若干服务,服务之间通过网络交互来完成用户的业务处 理,当前流行的微服务架构就是分布式系统架构。

分布式系统具体如下基本特点:
1、 分布性:每个部分都可以独立部署,服务之间交互通过网络进行通信,比如:订单服务、商品服务。
2、 伸缩性:每个部分都可以集群方式部署,并可针对部分结点进行硬件及软件扩容,具有一定的伸缩能力。
3、 共享性:每个部分都可以作为共享资源对夕龍供服务,多个部分可能有操作共享资源的情况。
4、 开放性:每个部分根据需求都可以对夕卜发布共享资源的访问接口 ,并可允许第三方系统访问。

5.2 分布式认证需求

分布式系统的每个服务都会有认证、授权的需求,如果每个服务都实现一套认证授权逻辑会非常冗余,考虑分布式 系统共享性的特点,需要由独立的认证服务处理系统认证授权的请求;考虑分布式系统开放性的特点,不仅对系统 内部服务提供认证,对第三方系统也要提供认证。分布式认证的需求总结如下:
统一认证授权
提供独立的认证服务,统一处理认证授权。
无论是不同类型的用户,还是不同种类的客户端(web端,H5、APP),均采用一致的认证、权限、会话机制,实现 统一认证授权。
要实现统一则认证方式必须可扩展,支持各种认证需求,比如:用户名密码认证、短信验证码、二维码、人脸识SU 等认证方式,并可以非常灵活的切换。
应用接入认证 应提供扩展和开放能力,提供安全的系统对接机制,并可开放部分API给接入第三方使用,一方应用(内部系统服 务)和三方应用(第三方应用)均采用统一机制接入。
5.3分布式认证方案
5.3.1选型分析
1、基于session的认证方式
在分布式的环境下,基于session的认证会出现一个问题,每个应用服务都需要在session中存储用户身份信息,通 过负载均衡将本地的请求分配到另一个应用服务需要将session信息带过去,否则会重新认证。

这个时候,通常的做法有下面几种:
Session复制:多台应用服务器之间同步session ,使session保持一致,对外透明。
Session黏贴:当用户访问集群中某台服务器后,强制指定后续所有请求均落到此机器上。
Session集中存储:将Session存入分布式缓存中,所有服务器应用实例统一从分布式缓存中存取Session。
总体来讲,基于session认证的认证方式,可以更好的在服务端对会话进行控制,且安全性较高。但是,session机 制方式基于cookie ,在复杂多样的移动客户端上不能有效的使用,并且无法跨域,另夕卜随着系统的扩展需提高 session的复制、黏贴及存储的容错性。
2、基于token的认证方式
基于token的认证方式,服务端不用存储认证数据,易维护扩展性强,客户端可以把token存在任意地方,并且可 以实现web和app统一认证机制。其缺点也很明显,token由于自包含信息,因此一般数据量较大,而且每次请求 都需要传递,因此比较占带宽。另夕卜,token的签名验签操作也会给cpu带来额夕卜的处理负担。

5.3.2技术方案
根据选型的分析,决定采用基于token的认证方式,它的优点是:
1、 适合统一认证的机制,客户端、一方应用、三方应用都遵循一致的认证机制。
2、 token认证方式对第三方应用接入更适合,因为它更开放,可使用当前有流行的开放协议Oauth2.0、等。
3、 一般情况服务端无需存储会话信息,减轻了服务端的压力。
分布式系统认证技术方案见下图:
接入方

WEB
移动端
第三方
接入
5.携帝切ken访冋资源
4.返回 access_t° 値 n
7•附加解析后明文token,转发讷遊到服务

资源
6.按验token,验证接入客户端权銀
L接入方讷求授权服务认证
8.解析井號证用户权限.若逐过则执行业务逻辑|
认证
俱验证登录用户京接入客户端
微服务■
〔资源}
微服务2
隨源}
微服务3
■源}
微服务n
隨源}

流聞述:
(1 )用户通过接入方(应用)登录,接入方采取OAuth2.0方式在统一认证服务(UAA)中认证。
(2 )认证服务(UAA)调用验证该用户的身份是否合法,并获取用户权限信息。
(3 )认证服务(UAA)获取接入方权限信息,并验证接入方是否合法。
(4 )若登录用户以及接入方都合法,认证服务生成jwt令牌返回给接入方,其中jwt中包含了用户权限及接入方权 限。
(5 )后续,接入方携带jwt令牌对API网关内的微服务资源进行访问。
(6 ) API网关对令牌解析、并验证接入方的权限是否能够访问本次请求的微服务。
(7)如果接入方的权限没问题,API网关将原请求header中附加解析后的明文Token ,并将请求转发至糊艮务。
(8 )糊艮务收到请求,明文token中包含登录用户的身份和权限信息。因此后续微服务自己可以干两件事:1 ,用 户授权拦截(看当前用户是否有权访问该资源)2 ,将用户信息存储进当前线程上下文(有利于后续业务逻辑随时 获取当前用户信息)
流程所涉及到UAA服务、API网关这三个组件职责如下:
1 )统一认证服务(UAA)
它承载了OAuth2.0接入方认证、登入用户的认证、授权以及生成令牌的职责,完成实际的用户认证、授权功能。
2) API网关
作为系统的唯一入口 , API网关为接入方提供定制的API集合,它可能还具有其它职责,如身份验证、监控、负载均 衡、缓存等。API网关方式的核心要点是,所有的接入方和消费端都通过统一的网关接入微服务,在网关层处理所 有的非业务功能。

六、Oauth 2.0

OAuth2.0
6.1 OAuth2.0 介绍
OAuth (开放授权)是一个开放标准,允许用户授权第三方应用访问他们存储在另外的服务提供者上的信息,而不 需要将用户名和密码提供给第三方应用或分享他们数据的所有内容。OAuth2.0是OAuth协议的延续版本,但不向 后兼容OAuth 1.0即完全废止了OAuth 1.0。很多大公司如Google , Yahoo , Microsoft等都提供了OAUTH认证^ 务,这些都足以说明OAUTH标准逐渐成为开放资源授权的标准。
Oauth协议目前发展到2.0版本,1.0版本过于复杂,2.0版本已得到广泛应用。
参考:httDS:〃baike.baidu.com/item/oAuth/7153134?fr=aladdin
Oauth协议:httDS://tools.ietf.org/html/rfc6749
下边分析一个Oauth2认证的例子,通过例子去理解OAuth2.0协议的认证流程,本例子是黑马程序员网站使用微信 认证的过程,这个过程的简要描述如下:

用户借助微信认证登录黑马程序员网站,用户就不用单独在黑马程序员注册用户,怎么样算认证成功吗?黑马程序 员网站需要成功从微信获取用户的身份信息则认为用户认证成功,那如何从微信获取用户的身份信息?用户信息的 拥有者是用户本人,微信需要经过用户的同意方可为黑马程序员网站生成令牌,黑马程序员网站拿此令牌方可从微 信获取用户的信息。
1、客户端请求第三方授权
用户进入黑马程序的登录页面,点击微信的图标以微信账号登录系统,用户是自己在微信里信息的资源拥有者。
傍)黑马程序员、
VI www.itheima.com

点击"微信"出现一个二维码,此时用户扫描二维码,开始给黑马程序员授权。

瞬扫描
”黑马程序员用户中,CT
2、资源拥有者同意给客户端授权
资源拥有者扫描二维码表示资源拥有者同意给客户端授权,微信会对资源拥有者的身份进行验证,验证通过后,微 信会询问用户是否给授权黑马程序员访问自己的微信数据,用户点击"确认登录"表示同意授权,微信认证服务器会 颁发一个授权码,并重定向到黑马程序员的网站。
0
黑马程序貝
黑马程序员用户中心
即将登录黑马程序员用户中心,请确 认是本人操作
•使用你的帐号登录该应用
确认登录
取消
3、 客户端获取到授权码,请求认证服务器申请令牌
此过程用户看不到,客户端应用程序请求认证服务器,请求携带授权码。
4、 认证服务器向客户端响应令牌
微信认证服务器验证了客户端请求的授权码,如果合法则给客户端颁发令牌,令牌是客户端访问资源的通行证。
此交互过程用户看不到,当客户端拿到令牌后,用户在黑马程序员看到已经登录成功。
5、 客户端请求资源服务器的资源
客户端携带令牌访问资源服务器的资源。
黑马程序员网站携带令牌请求访问微信服务器获取用户的基本信息。
6、 资源服务器返回受保护资源
资源服务器校验令牌的合法性,如果合法则向用户响应资源信息内容。
以上认证授权详细的执行流程如下:

I一一 (E.J Access Token > |
I <- (F) Prot.ert.ed Resource |
+ +
OAauth2.0包括以下角色:
1、 客户端
本身不存储资源,需要通过资源拥有者的授权去请求资源服务器的资源,比如:Android客户端、Web客户端(浏 览器端)、微信客户端等。
2、 资源拥有者
通常为用户,也可以是应用程序,即该资源的拥有者。
3、 授权服务器(也称认证服务器)

用于服务提供商对资源拥有的身份进行认证、对访问资源进行授权,认证成功后会给客户端发放令牌
(access_token ),作为客户端访问资源服务器的凭据。本例为微信的认证服务器。
4、资源服务器
存储资源的服务器,本例子为微信存储的用户信息。
现在还有一个问题,服务提供商能允许随便一个客户端就接入到它的授权服务器吗?答案是否定的,服务提供商会 给准入的接入方一个身份,用于接入时的凭据:
clientjd :客户端标识client_secret :客户端秘钥
因此,准确来说,授权服务器对两种OAuth2.0中的两个角色进行认证授权,分别是资源拥有者、客户端。
6.2 Spring Cloud Security OAuth2
6.2.1环境介绍
Spring-Security-OAuth2是对OAuth2的一种实现,并且跟我们之前学习的Spring Security相辅相成,与Spring Cloud体系的集成也非常便利,接下来,我们需要对它进行学习,最终使用它来实现我们设计的分布式认证授权解 决方案。
OAuth2.0的服务提供方涵盖两个服务,即授权服务(Authorization Server,也叫认证服务)和资源服务(Resource Server),使用Spring Security OAuth2的时候你可以选择把它们在同一个应用程序中实现,也可以选择建立使用 同一个授权服务的多个资源服务。
授权服务(Authorization Server)应包含对接入端以及登入用户的合法性进行验证并颁发to ken等功能,对令牌 的请求端点由Spring MVC控制器进行实现,下面是配置一个认证服务必须要实现的endpoints :
・AuthorizationEndpoint 服务于认证请求。默认 URL : /oauth/authorize 0
, TokenEndpoint服务于访问令牌的请求。默认URL: /oauth/token o
资源服务(Resource Server),应包含对资源的保护功能,对非法请求进行拦截,对请求中token进行解析鉴 权等,下面的过滤器用于实现OAuth 2.0资源^务:
•OAuth2AuthenticationProcessingFilter用来对请求给出的身份令牌解析鉴权。
本教程分别创建uaa授权务(也可叫认证务)和orders单资源服务。

认证流程如下:
1、客户端请求UAA授权服鼬行认证。
2、认证通过后由UAA颁发令牌。
3、客户端携带令牌Token请求资源服务。

传智播客旗下 高端IT教育品牌
4、资源服务校验令牌的合法性,合法即返回资源信息。
6.2.2环境搭建
6.2.2.1父工程
创建maven工,呈作为父工程,依赖如下:

<?xml version="1.0" encoding="UTF-8,,?>


4.0.0
com.itheima.security
distributed-security
1.0-SNAPSHOT
pom

org.springframework.boot
spring-boot-starter-parent
2.1.3.RELEASE


<project.build , sourceEncoding>UTF-8</project , build.sourceEncoding>
<project , reporting.outputEncoding>UTF-8</project , reporting,outputEncoding〉
<java.version>1.8</java.version>




org.springframework.cloud
spring-cloud-dependencies
Greenwich , RELEASE
pom
import


javax.servlet
javax.servlet-api
3.1.0
provided


javax.interceptor
javax.interceptor-api
1.2


com.alibaba
fastjson
1.2.47


org.projectlombok
lombok
1.18.0


mysql
mysql-connector-java
5.1.47


org.springframework.security
spring-security-jwt
1.0.10.RELEASE


org.springframework.security.oauth.boot
spring-security-oauth2-autoconfigure
2.1.3.RELEASE




${project.name}
src/main/resources true

/*



src/main/java

〈include〉/*.xml



org.apache.maven.plugins maven-compiler-plugin 1.8 l.8 maven-resources-plugin utf-8 true 6.2.2.2创建UAA授权服务工程 1、创建distributed-security-uaa 创建distributed-security-uaa作为授权服务工程,依赖如下: <?xml version="1.0" encoding="UTF-8,,?> distributed-security com.itheima.security 1.0-SNAPSHOT 4.0.0 distributed-security-uaa <! -- org.springframework.cloud spring-cloud-starter-netflix-eureka-client --> org.springframework.cloud spring-cloud-starter-netflix-hystrix org.springframework.cloud spring-cloud-starter-netflix-ribbon org.springframework.cloud spring-cloud-starter-openfeign com.netflix.hystrix hystrix-javanica org.springframework.retry spring-retry org.springframework.boot spring-boot-starter-actuator org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-freemarker org.springframework.data spring-data-commons org.springframework.cloud spring-cloud-starter-security org.springframework.cloud spring-cloud-starter-oauth2 org.springframework.security spring-security-jwt javax.interceptor javax.interceptor-api mysql mysql-connector-java org.springframework.boot spring-boot-starter-jdbc com.alibaba fastjson org.projectlombok lombok 工曜吉构如下: distributed^secuxity^uaa B di s tr ibut e d-s e cur i ty-uaa. iml III pom.xml v src v main v java v com v 、 itheima v security v distributed v 二 uaa config dao >X model >X service UAAServex ■Bresouxces > test 2、 启动类 本工程采用SpringBoot开发,每个工程编写一个启动类: @SpringBootApplication @EnableDiscoveryClient @EnableHystrix @EnableFeignClients(basePackages = ("com.itheima.security.distributed.uaa"}) public class UAAServer { public static void main(String[] args) { SpringApplication・run(UAAServer.class, args); } } 3、 配置文件 在 resources 下仓[]建application.properties spring.application , name=uaa-service server.port=53020 spring.main.allow-bean-definition-overriding = true logging.level.root = debug logging.level.org.springframework.web = info spring.http.encoding.enabled = true spring.http.encoding.charset = UTF-8 spring.http.encoding.force = true

server.tomcat.remote_ip_header = x-forwarded-for server.tomcat.protocol_header = x-forwarded-proto server.use-forward-headers = true server.servlet.context-path = /uaa spring.freemarker.enabled = true
spring.freemarker.suffix = .html
spring.freemarker.request-context-attribute = rc
spring.freemarker.content-type = text/html
spring.freemarker.charset = UTF-8
spring.mvc.throw-exception-if-no-handler-found = true spring.resources.add-mappings = false spring.datasource.url = jdbc:mysql://localhost:3306/user_db?useUnicode=true spring.datasource.username = root
spring.datasource.password = mysql
spring.datasource.driver-class-name = com.mysql.jdbc.Driver
#eureka. client. servicellrl. defaultzone = http://localhost: 53000/eureka/
#eureka.instance.preferlpAddress = true
#eureka.instance.instance-id = {{spring.application.name): s p r i n g . c l o u d . c l i e n t . i p − a d d r e s s : {spring.cloud.client.ip- address} : spring.cloud.client.ipaddress:( spring, application .instanceid:${server.port}}
management.endpoints.web.exposure.include feign.hystrix.enabled = true
feign.compression.request.enabled = true
feign.compression.request.mime-types[0]=
feign.compression.request.mime-types[l]=
feign.compression.request.mime-types[2]= feign.compression.request.min-request-size = 2048 feign.compression.response.enabled = true
6.2.23创建Order资源服务工程
本工程为Order订单服务工程,访问本工程的资源需要认证通过。
本工程的目的主要是测试认证授权的功能,所以不涉及订单管理相关业务。
1、创建Order工程

<?xml version="1.0" encoding="UTF-8,,?>



distributed-security
com.itheima.security
1.0-SNAPSHOT

4.0.0
distributed-security-order

<! –
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
–>

org.springframework.boot
spring-boot-starter-actuator


org.springframework.boot spring-boot-starter-web


org.springframework.cloud
spring-cloud-starter-security


org.springframework.cloud
spring-cloud-starter-oauth2


javax.interceptor
javax.interceptor-api


com.alibaba
fastjson


org.projectlombok
lombok



2、工曜吉构
di s tx ibut ed=seciu:ity-ox dex
B distributed-security-order, iml jTI pom. xml
V SIC
v main
v java
v Q com.
v itheima
V security
V distributed
V order

U config
E controller 區 OrdexS erver
v resources
(S) application, properties
test
3、配置文件
在 resources 中仓ij 建 application.properties
spring.application.name=order-service
server.port=53021
spring.main.allow-bean-definition-overriding = true
logging.level.root = debug
logging.level.org.springframework.web = info spring.http.encoding.enabled = true
spring.http.encoding.charset = UTF-8
spring.http.encoding.force = true
server.tomcat.remote_ip_header = x-forwarded-for server.tomcat.protocol_header = x-forwarded-proto server.use-forward-headers = true
server.servlet.context-path = /order
spring.freemarker.enabled = true spring.freemarker.suffix = .html
spring.freemarker.request-context-attribute = rc
spring.freemarker.content-type = text/html
spring.freemarker.charset = UTF-8
spring.mvc.throw-exception-if-no-handler-found = true
spring.resources.add-mappings = false
#eureka.client , serviceUrl.defaultzone = http://localhost:53000/eureka/
#eureka.instance.preferipAddress = true
#eureka.instance , instance-id = KaTeX parse error: Expected 'EOF', got '}' at position 27: …lication ・ name}̲:(spring.cloud.client ・ ip- address} : ( s p r i n g , a p p l i c a t i o n . i n s t a n c e i d : ( spring, application .instanceid: (spring,application.instanceid:{server.port}} management.endpoints.web.exposure.include feign.hystrix.enabled = true
feign.compression.request.enabled = true
feign.compression.request.mime-types[0]=
feign.compression.request.mime-types[l]=
feign.compression.request.mime-types[2]= feign.compression.request.min-request-size = 2048 feign.compression.response.enabled = true
6.2.2.授权服务器配置
6.2.2.1 EnableAuthorizationServer
可以用 @EnableAuthorizationServer 注解并继承AuthorizationServerConfigurerAdapter来酉己置OAuth2.0 授权 服务器。
在Config包下仓腱AuthorizationServer :
@Configuration
@EnableAuthorizationServer
public class Authorizationserver extends
AuthorizationServerConfigurerAdapter {
〃略・・・
}
AuthorizationServerConfigurerAdapte凄求配置以下几个类,这几个类是由Spring创建的独立的配置对象,它们 会被 Spring 传入 AuthorizationServerConfigurer 中进行配置。
public class AuthorizationServerConfigurerAdapter implements AuthorizationServerConfigurer { public AuthorizationServerConfigurerAdapter() {)
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {)
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {) public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {)
}
•ClientDetailsServiceConfigurer :用来配置客户端详情服务(ClientDetailsService ),客户端详情信息在 这里进行初始化,你能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息。
•AuthorizationServerEndpointsConfigurer :用来配置令牌(token )的访问端点和令牌服务(token services)。
•AuthorizationServerSecurityConfigurer :用来配置令牌端点的安全约束.
6.2.2.1.配置客户端详细信息
ClientDetailsServiceConfigurer 能够使用内存或者JDBC来实现客户端详情服务(ClientDetailsService ), ClientDetailsService负责查找ClientDetails ,而ClientDetails有几个重要的属性如下歹!]表:
•clientld:(必须的)用来标识客户的Id。
•secret :(需要值得信任的客户端)客户端安全码,如果有的话。
-scope :用来限制客户端的访问范围,如果为空(默认)的话,那么客户端拥有全部的访问范围。
•authorizedGrantTypes :此客户端可以使用的授权类型,默认为空。
•authorities :此客户端可以使用的权限(基于Spring Security authorities ) 0
客户端详情(Client Details )能够在应用程序运行的时』矣进行更新,可以通过访问底层的存储服务(例如将客户 端详’情存储在一个关系数据库的表中,就可以使用JdbcClientDetailsService )或者通过自己实现 ClientRegistrationService接口(同时你也可以实现 ClientDetailsService 接口)来进行管理。
我们暂时使用内存方式存储客户端详情信息,配置如下:
@Override
public void configure(ClientDetailsServiceConfigurer clients)
throws Exception {
// clients.withClientDetails(clientDetailsService);
clients.inMemory()// 使用in-memory視诸
.withClient(“cl”)// client_id
.secret(new BCryptPasswordEncoder().encode(“secret”))
.resourcelds(“resl”)
.authorizedGrantTypes(“authorization_code”
“passwordu} “client^redentials”“implicit”“refresh_token”)// 该client允许的授权类型 authorization_codej password,refresh_tokenimplicitclient_credentials
.scopesCall”)//允许的授权范围
.autoApprove(false)
〃加上验证回调地址
.redirectUris(“http://www.baidu.com”);
}
6.2.2.2.管理令牌
AuthorizationServerTokenServices接口定义了一些操作使得你可以对令牌进行一些必要的管理,令牌可以被用来 加载身份信息,里面包含了这个令牌的相关权限。
自己可以创建AuthorizationServerTokenServices这个接口的实现,则需要继承DefaultTokenServices这个类, 里面包含了一些有用实现,你可以使用它来修改令牌的格式和令牌的存储。默认的,当它尝试创建一个令牌的时 候,是使用随机值来进行填充的,除了持久化令牌是委托一个Tokenstore接口来实现以夕卜,这个类几乎帮你做了 所有的事情。并且Tokenstore这个接口有一个默认的实现,它就是InMemoryTokenStore ,如其命名,所有的 令牌是被保存在了内存中。除了使用这个类以夕卜,你还可以使用一些其他的预定义实现,下面有几个版本,它们都 实现了Tokenstore接口 :
•InMemoryTokenStore :这个版本的实现是被默认采用的,它可以完美的工作在单服务器上(即访问并发量 压力不大的情况下,并且它在失败的时"契行备份),大多数的项目都可以使用这个版本的实现来进行 尝试,你可以在开发的时候使用它来进行管理,因为不会被保存到磁盘中,所以更易于调试。
•JdbcTokenStore :这是一个基于JDBC的实现版本,令牌会被保存进关系型数据库。使用这个版本的实现时, 你可以在不同的服务器之间共享令牌信息,使用这个版本的时候请注意把"spring-jdbc"这个依赖加入^你的 classpath 当中。
•JwtTokenStore :这个版本的錄是JSON Web Token (JWT),它可以把令牌相关的瓣进行编码(因此对 于后端服务来说,它不需要进行存储,这将是一个重大优势),但是它有一个缺点,那就是扌詭肖一个已经授 权令牌将会^常困难,所以它通常用来处理一个生命周期较短的令牌以及撤销刷新令牌(refresh_token )。 另夕个缺点就是这个令牌占用的空间会比较大,如果你加入了比较多用户凭证信息。JwtTokenStore不会保
存任何数据,但是它在转换令牌值以及授权信息方面与DefaultTokenServices所扮演的角色是一样的。
1、 定义TokenConfig
在config包下定义TokenConfig ,我们暂时先使用InMemoryTokenStore ,生成一个普通的令牌。
@Configuration
public class TokenConfig {
@Bean
public TokenStore tokenStore() {
return new InMemoryTokenStore();
}
}
2、 ^^.AuthorizationServerTokenServices
在 AuthorizationServe r 中 ^^.AuthorizationServerTokenServices
@Autowired
private TokenStore tokenstore;
@Autowired
private ClientDetailsService clientDetailsService;
@Bean
public AuthorizationServerTokenServices tokenService() { DefaultTokenServices service=new DefaultTokenServices(); service.setClientDetailsService(clientDetailsService); service.setSupportRefreshToken(true);
service.setTokenStore(tokenStore);
service.setAccessTokenValiditySeconds(7200); // 令牌默认有效期2小时 service.setRefreshTokenValiditySeconds(259200); // 刷新令牌默认有效期3天 return service;
}
6.2.23.令牌访问端点配置
AuthorizationServerEndpointsConfigurer这个对象的实例可以完成令牌服务以及令牌endpoint配置。
配置授权类型(Grant Types )
AuthorizationServerEndpointsConfigurer通过设定以下属性决定支持的授权类型(Grant Types ):
•authenticationManager :认证管理器,当你圈圣了资源所有者密码(password )授权类型的时候,请设置 这个属性注入一个AuthenticationManager对象。
•userDetailsService :如果你设置了这个属性的话,那说明你有一个自己的UserDetailsService接口的实现, 或者你可以把这个东西设置至IJ全局域上面去(例如GlobalAuthenticationManagerConfigurer这个酉己置对 象),当你设置了这个之后,那么"refresh_token"即刷新令牌授权类型模式的流程中就会包含一个检查,用 来确保这个账缨否仍然有效,假如说你禁用了这个畔的话。
•authorizationCodeServices :这个属性是用来设置授权码服务的(即AuthorizationCodeServices的实例对 象),主要用于"authorization_code"授权码类型模式。
•implicitGrantService :这个属性用于设置Het授权模式,用来MSHeC授权模式的状态。
•tokenGranter :当你设置了这个东西(即TokenGranter接口实现),那么授权将会交由你来完全掌控,并 且会忽略掉上面的这几个属性,这个属性一般是用作拓展用途的,即标准的四种授权模式已经满足不了你的 需求的时候,才会考虑使用这个。
配置授权端点的URL ( Endpoint URLs ):
AuthorizationServerEndpointsConfigurer这个配置对象有一个叫做pathMappingO的方法用来配置端点URL链 接,它有两个参数:
・第一个参数:String类型的,这个端点URL的默认踊。
-第二个参数:String类型的,你要进行替代的URL踊。
以上的参数都将以"/“字符为开始的字符串,框架的默认URL踊如下列表,可以作为这个pathMappingO方法的 第一个参数:
•/oauth/authorize :授权端点。
•/oauth/token :令牌端点。
•/oauth/confirm_access :用户确认授权提交端点。
, /oauth/error :授权服务错误信息端点。
•/oauth/check_token :用于资源服务访问的令牌解析端点。
•/oauth/token_key :提供公有密匙的端点,如果你使用JWT令牌的话。
需要注意的是授权端点这个URL应该被Spring Security保护起来只供授权用户访问.
在Authorizationserver酉己置令牌访问端点
@Autowired
private AuthorizationCodeServices authorizationCodeServices;
@Autowired
private AuthenticationManager authenticationManager;
^Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) { endpoints
.authenticationlvlanager(authenticationlvlanager)
.authorizationCodeServices(authorizationCodeServices)
.tokenServices(tokenService())
.allowedTokenEndpointRequestMethods(HttpMethod.POST);
}
@Bean
public AuthorizationCodeServices authorizationCodeServices() { //设置授权码模式的授权码如何 存取,暂时采用内存方式
return new InMemoryAuthorizationCodeServices();
}
6.2.2.4.令牌端点的安全约束
AuthorizationServerSecurityConfigurer :用来酉巳置令牌端点(Token Endpoint)的安全约束,在 AuthorizationServe r 中配置如下.
^Override
public void configure(AuthorizationServerSecurityConfigurer security)!
security
.tokenKeyAccess(“permitAll()”) (1)
.checkTokenAccess(“permitAll()”) (2)
.allowFormAuthenticationForClients() (3)
»
}
(1 ) tokenkey这个endpoint当使用JwtToken且使用非对称加密时,资源服务用于获取公钥而开放的,这劉旨这个 endpoint完全公开。
(2 ) checkToken这个endpoint完全公开
(3)允许表单认证
授权服务配置总结:授曄务配置分成三大块,可以关联记忆。
既然要完成认证,它首先得知道客户端信息从哪儿读取,因此要进行客户端详情配置。
既然要颁发token ,那必须得定义token的相关endpoint,以及token如何存取,以及客户端支寺哪些类型的 token。
既然暴露除了一些endpoint,那对这些endpoint可以定义一些安全上的约束等。
6.2.2.5 web安全配置
将 Spring-Boot 工程中的 WebSecurityConfig 拷贝至!]UAA 工程中。
(^Configuration
@EnableGlobalMethodSecurity(securedEnabled = true^ prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean();
}
//安全拦截机制(最重要)
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
.antMatchersC/r/rl”). hasAnyAuthority( “pl”)
.antMatchers("/login*").permitAl1()
.anyRequest().authenticated()

.and()
.formLogin()
6.2.3.授权码模式
6.2.3.1授权码模式介绍
下图是授权码模式交互图:
资源
拥有者

(1)资源拥有者打开客户端,客户端要求资源拥有者给予授权,它将浏览器被重定向到授权服务器,重定向时会 附加客户端的身份信息。如:
/uaa/oauth/authorize?client_id=cl&response_type=code&scope=all&redirect_uri=http://www.baidu.com
参数列表如下:
, clientjd :客户端准入标识。
, response_type :授权码模式固定为code。
• scope :客户端权限。
-redirect_uri :驱专uri ,当授权码申请成功后会驱专到此地址,并在后边带上code参数(授权码)。
(2 )浏览器出现向授权服务器授权页面,之后将用户同意授权。
(3 )授权服务器:阁§权码(Authorizationcode )转经浏览器发送给client(通过redirect_uri)。
(4)客户端拿着授权码向授权服务器素要访问access_token ,请求如下:
/uaa/oauth/token? client_id=cl&client_secret=secret&grant_type=authorization_code&code=5PgfcD&redirect_uri=http://w
ww.baidu.com
参数列表如下
, clientjd :客户端准入标识。
•client_secret :客户端秘钥。
, grant_type :授权类型,填写authorization_code ,表示授权码模式
•code :授权码,孵刚冈蟻取的授权码,注意:授权码只使用一次赧效了 ,需要重新申请。
•redirect_uri :申请授权码时的物辟专url ,—定和申请授权码时用的redirect_uri一致。
(5 )授权服务器返回令牌(access_token)
这种模式是四种模式中最安全的一种模式。一般用于client是Web服务器端应用或第三方的原生App调用资源^务 的时候。因为在这种模式中access_token不会经过浏览器或移动端的App ,而是直接从^务端去交换,这样就最大 限度的减小了令牌泄漏的风险。
6.2.3.2 测试
浏览器访问认证页面:
http://localhost:53020/uaa/oauth/authorize?
client_id=cl&response_type=code&scope=all&redirect_uri=http://www.baidu.com
Please sign in
Username
Password
Sign in
鴻输入模拟的账号和密码点登陆之后进入授权页面:
OAuth Approval
Do you authorize “cl” to access your protected resources?
• scope.all: * Approve O Deny
Authorize
确认授权后,浏览器会重定向到指定路径(oauth_client_details表中的web_server_redirect_uri )并附加验证码? code=DB2mFj (每次不一样),最后使用该验证码获取to ken。
POST http://localhost:53020/uaa/oauth/token
http://l!ocalhosc: 53020/uaa/oauth /token
Value

client_credentials
Badly Cookies (3) Headers (8) Test Results
Pretty Raw Preview J SON V zp
「{
,access.token "eSydSbf 5-f6S8-4c6f-bca5-2fc5767a85bcf’」
“token^ype” : “bearer” f
“expires^n” : 7199 ,
5’“scope”:
6}

客户端
(1)资源拥有者打开客户端,客户端要求资源拥有者给予授权,它将浏览器被重定向到授权服务器,重定向时会 附加客户端的身份信息。如:
/uaa/oauth/authorize?client_id=cl&response_type=token&scope=all&redirect_uri=http://www.baidu.com
参数描述同授权码模式,注意response_type=token ,说明是简化模式。
(2)浏览器出现向授权服务器授权页面,之后将用户同意授权。
(3 )授权服务器:|她权码将令牌(access_token )以Hash的形式存放在重定向uri的fargment中发送给浏览 器。
注:fragment主要是用来标识URI所标识资源里的某个资源,在URI的末尾通过(# )作为fragment的开头, 其中#不属于fragment的值。如httDS:〃domain/index#L18i文个URI中L18就是fragment的值。大家只需要 知道js通过响应浏览器地址栏变化的方式能获取到fragment就行了。
一般来说,简化模式用于没有服务器端的第三方单页面应用,因为没有服务器端就无法接收授权码。
6.2.4.2 测试
浏览器访问认证页面:
http://localhost:53020/uaa/oauth/authorize?
client_id=cl&response_type=token&scope=all&redirect_uri=http://www.baidu.com
Please sign in
Username
Password
Sign in
鴻输入模拟的账号和密码点登陆之后进入授权页面:
OAuth Approval
Do you authorize "cl1’ to access your protected re sou rces?
• scope.all: ■* Approve Deny
Authorize
确认授权后,浏览器会重定向到指定路径(oauth_client_details表中的web_server_redirect_uri )并以Hash的形 式存放在重定向uri的fargment中,如:
http://aa.bb.cc/receive#access_token=eyJhbGciOi3IUzIlNiIsInR5cCI6IkpXVCJ9.eyJ0ZW5hbn…
6.2.5.密码模式
6.2.5.1授权码模式介绍
下图是密码模式交互图:
授权服务器
(1 )资源拥有者将用户名、密码发送给客户端
(2 )客户端拿着资源拥有者的用户名、密码向授权服务器请求令牌(access_token ),请求如下:
/uaa/oauth/token?
client_id=cl&client_secret=secret&grant_type=password&username=shangsan&password=123
参数列表如下:
•clientjd :客户端准入标识。
•client_secret :客户端秘钥。
•grant_type :授权类型,填写password表示密码模式
•username :资源拥有者用户名。
•password :资源拥有者密码。
(3 )授权服务器将令牌(access_token )发送给client
这种模式十分简单,但是却意味着頡将用户敏感信息泄漏给了client,因此这就说明这种模式只能用于client是我 们自己开发的情况下。因此密码模式一般用于我们自己开发的,第一方原生App或第一方单页面应用。
6.2.5.2 测试
POST http://localhost:53020/uaa/oauth/token
请求参数:

Authorization Headers (1) Body • Pre-request Script Tests

Key Value Description
Q dient.id c1
3 client_secret secret
Q username zhangsan
2 password 123
J grant_type password
返回结果:
Body Cookies (3) Headers (8) Test Results
Pretty Raw Preview JSON V zp
“access_token”: ,e27d9bf5-f608-4c6f-bca5-2fc5767a85bc", “token_type”: “bearer”,
4"expires_in": 993,
5"scope"
6}
6.2.6.客户端模式
6.2.6.1客户端模式介绍
客户端身傍

(1 )客户端向授权服务器发送自己的身份信息,并请求令牌(access_token )
(2 )确认客户端身份无误后,将令牌(access_token )发送给client,请求如下:
/uaa/oauth/token?client_id=cl&client_secret=secret&grant_type=client_c redentials
参数列表如下:
, clientjd :客户端准入标识。
•client_secret :客户端秘钥。
•grant_type :授权类型,填写client_credentials表示客户端模式
这种模式是最方便但最不安全的模式。因此这就要求我们对client完全的信任,而client本身也是安全的。因 此这种模式一般用来提供给我们完全信任的服务器端服务。比如,合作方系统对接,拉取一组用户信息。

6.2.6.2客户端模式介绍
POST http://localhost:53020/uaa/oauth/token
请求参数:
POST v http^/localhose53020/uaa/oauth/token
Send
Authorization Headers (1)
Body • Pre-request Script Tests
form-data . x-www-form-urlencoded
raw 。binary
Key
Q grant_type
Q dientjd
3 client_secret
Value
dient_credentials
c1
secret
Description
返回结果:
Pretty Raw Preview JSON V zp
“access_token”: ■,5eec55b0-d67c-480J-b8d8-67e0ac0279c4t,,
“token_type”: “bearer”, “expires_in”: 71991 "scope0
6 }
6.2.7.资源服务测试
6.2.7.1资源服务器配置
@EnableResourceServer 注解到一个 ©Configuration 配置类上,并且必须使用 ResourceServerConfigurer 这个 配置对象来进行配置(可以选择继承自ResourceServerConfigurerAdapter然后覆写其中的方法,参数就是这个 对象的实例),下面是一些可以配置的属性:
ResourceServerSecurityConfigurer中主要包括:
•tokenServices : ResourceServerTokenServices 类的实例,用来实现令牌服务。
•tokenstore : TokenStore类的实例,指定令牌如何访问,与tokenServices酉己置可选
.resourceld :这个资源服务的ID ,这个属性是可选的,但是推荐设置并在授权^务中进行验证。
,其他的拓展属性例如tokenExtractor令牌提取器用来提取请求中的令牌。
HttpSecurity酉己置这个与Spring Securit殘似:
-请求匹配器,用来设置需要进行保护的资源路径,默认的情况下是例户资源服务的全部路径。
•通过http.authorizeRequests()来设置受保护资源的访问规则
•其他的自定义权限保护规则iffid HttpSecurity来进行配置。
@EnableResourceServer 注解自动增加了一个类型为 OAuth2AuthenticationProcessingFHter 的过滤器链
编写ResouceServerConfig :

import org.springframework.context.annotation,Bean;
import org.springframework.context.annotation.Configuration;
import
org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import
org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; import
org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerA dapter;
import
org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfi gurer;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices;
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResouceServerConfig extends
ResourceServerConfigurerAdapter {
public static final String RESOURCE_ID = “resl”;
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceld(RESOURCEID)
.tokenServices(tokenService())
.stateless(true);
}
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers( '7**1).access("#oauth2.hasScope(‘all’)")
.and().csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
}
6.2.7.2 验证 token
ResourceServerTokenServices是组成授权服务的另一半,如果你的授权服务和资源服务在同一个应用程序上的 话,你可以使用DefaultTokenServices ,这样的话,你就不用考虑关于实现所有必要的接口的一致性问题。如果 你的资源服务器是分离开的,那么你就必须要确保能够有匹配授权服务提供的ResourceServerTokenServices ,它 知道如何对令牌进行解码。
令牌解析方¥去:使用DefaultTokenServices在资源服务器本地配置令牌存储、解码、解析方式使用 RemoteTokenServices资源服务器通过HTTP请求来解码令牌,每次都请求授权服务器端点/oauth/check_token
使用授权服务的/oauth/check_token端点你需要在授权服务将这个端点暴露出去,以便资源服务可以进行访问, 这在咱们授权服务配置中已经提到了 ,下面是一个例子,在这个例子中,我们在授权服务中配置了 /oauth/check_token 和 /oauth/token_key 这两个端点:
^Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security
.tokenKeyAccess(“permitAll()”)// /oauth/token_key 安全配置
.checkTokenAccess(,permitAll()") // /oauth/check_token 安全配置
}
在资源服务配置RemoteTokenServices ,在ResouceServerConfig中配置:
〃资源服务令牌解析服务
@Bean
public ResourceServerTokenServices tokenService() {
〃使用远程服务请求授权服务器校验token,必须指定校验token的u”、client_id , client_secret RemoteTokenServices service=new RemoteTokenServices。;
service.setCheckTokenEndpointUrl(“http://localhost:53020/uaa/oauth/check_token”);
service.setClientId(“cl”);
service.setClientSecret(“secret”);
return service;
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) {
resources.resourceId(RESOURCE_ID)
.tokenServices(tokenService())
.stateless(true);
}
627.3编写资源
在controller包下编写OrderController,此controller表示订单资源的访问类:
@RestController
public class OrderController {
@GetMapping(value - “/rl”)
@PreAuthorize(nhasAnyAuthority(‘pl’)")
public String rl(){ return "访问资源1”;
}
}
6.2.7.4添加安全访问控制

(^Configuration
@EnableGlobalMethodSecurity(securedEnabled = true^ prePostEnabled = true) public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
//安全拦截机制(最重要)
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeRequests()
// .antMatchers("/r/rl").hasAuthority(’^2")
// .antMatchers("/r/rZ").hasAuthority(“p2”)
.antMatchers("/r/**") .authenticated])//所有/p/的请求必须认证通过 .anyRequest(). permitAll( )//|^T/r/ ,其它的请求可以访问
627.5测试
1、申请令牌
这里我们使用密码方式
http://local host:53020/uaa/oauth/token
cl
secret
zhangsan
123
password
响应结果:
{
“access_token”: “e3360db3-2d85-41c9-ac5e-b9adba48e26c”
“token_type”: “bearer”
“expires_in”: 7199,
“scope”: “all”
}
2、请求资源
按照。auth2.0协议要求,请求资源需要携带token ,如下:
token的参数名称为:Authorization ,值为:Bearer token值

Bearer e3360db3-2d85-41 c9-ac5e-b9adba48e26c
Body Cookies ⑶ Headers (9) Test Results
Pretty Raw Preview Text 7 zp
-访问资源1
如果token错误,则授权失败,如下:
“error”: “invalid_token”
“error_description”: “f3360db3-2d85-41c9-ac5e-b9adba48e26c”
}
6.3 J WT令牌
6.3.1 JWT 介绍
通过上边的测试我们发现,当资源服务和授权^务不在一起时资源服务使用RemoteTokenServices碰请求授权 服务验证token ,如果访问量较大将会影响系统的性能。
解决上边问题:
令牌采用JV0格式即可解决上边的问题,用户认证通过会得到一个JWT令牌,JWT令牌中已经包括了用户相关的信 息,客户端只需要携带JWT访问资源服务,资源^务根据事先约定的算法自行完成令牌校验,无需每次都请求认证 服务完成授权。
1、什么是JWT?
JSON Web Token (JWT)是一个开放的行业标准(RFC 7519),它定义了一种简介的、自包含的协议格式,用于 在通信双方传递json对象,传递的信息经过数字签名可以被验证和信任。JWT可以使用HMAC算法或使用RSA的公 钥/私钥对来签名,防止被篡改。
官网:httDS://jwt.io/
标准:httDS://tools.ietf.org/html/rfc7519
JWT令牌的优点:
1) jwt基于json ,非常方便解析。
2)可以在令牌中自定义丰富的内容,易扩展。
3)鮑非对称加密算法験字签名技术,JM防修改,安全性高。
4)资源^务使用JM可不依赖认碘务即可完成授权。
缺点:
1 ) JWT令牌较长,占存储空间比较大。
2、JWT令牌结构
通过学习JM令牌结构为自定义jwt令牌打好基础。
JWT令牌由三部分组成,每部分中间使用点(.)分隔,比如:xxxxx.yyyyy.zzzzz
•Header
头部包括令牌的类型(即JWT )及使用的哈希算法(如HMAC SHA256或RSA )
一个例子如下:
下边是Header部分的内容

“alg”: “HS256”,
“typ”: “JWT”
}
将上边的内容使用Base64Url编码,得到一个字符串就是令牌的第一部分。
•Payload
第二部分是负载,内容也是一个json对象,它是存放有效信息的地方,它可以存放jwt提供的现成字段,比 如:iss (签发者),exp (过期时间戳),sub (面向的用户)等,也可自定义字段。
此部分不建议存放敏感信息,因为此部分可以解码还原原始内容。
最后将第二部分负载使用Base64Url编码,得到一个字符串就是JWT令牌的第二部分。
一个例子:
{
“sub”: “1234567890”,
“name”: “456”, “admin”: true
}
•Signature
第三部分是签名,此部分用于防止jwt内容被篡改。
这个部分使用base64url将前两部分进行编码,编码后使用点(.)连接组成字符串,最后使用header中声明 签名算法进行签名。
一个例子:
HMACSHA256(
base64UrlEncode(header) + “.” +
base64UrlEncode(payload)
secret)
base64UrlEncode(header) : jwt令牌的第一部分。 base64UrlEncode(payload) : jwt令牌的第二部分。 secret :签名所使用的密钥。
6.3.2配MJWT令牌服务
在uaa中配置jwt令牌服务,即可实现生成jwt格式的令牌。
1、 TokenConfig
@Configuration
public class TokenConfig {
private String SIGNING_KEY = “uaal23”;
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
3wtAccessTokenConventer converter = new JwtAccessTokenConverter();
converter.setSigningKey(SIGNING_KEY); 〃对称秘钥,资源服务器使用该秘钥来验证 return converter;
}
}
2、 定义JWT令牌服务
@Autowired
private 3wtAccessTokenConventer accessTokenConverter;
@Bean
public AuthorizationServerTokenServices tokenService() {
DefaultTokenServices service=new DefaultTokenServices();
service.setClientDetailsService(clientDetailsService);
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;
6.3.3生成jwt令牌

Authorization Headers (1) Body • Pre-request Script Tests (
'、-)form-data • x-www-form-urlencoded (丿 raw 。binary
Key Value Description
Q clientjd

Q client_secret
,username

Q grant_type
响应:
Alt
2 Bacce5s_tokern: “eyJtibGciOiJIUzIlNiIsInR5cCI6IkpXVCJ9 .ey3h(1WQiOlsicmV2MSJdLC31c2V^X25ht)WUiOiLlvlCDkuIkiLCJzY29wZSI6WyJribGwiXSwiZXiiwIjoxNTY5Nz¥zODI2LCJridXRob33pdGllcyI6Wy3wlwl5IsInAzI10sIfflp0aSI6IjUyMGI2CWNinLTY5N zItNGQ0NS05¥WU0LTg5YTZjMTY4NGIlMiIsImN5aWVudF9pZCI6IniMxIn0.ZV40qyMby4Pvcg04tk2aLvRp6S7Lu6xeMP-ZQp9diNM,J
“toker_type”: “bearer”,
4 “refresh_token” : ,eyJhbCciOiJIUzIlNiIsInR5cCI6IkpXVCJ9 .eyJhdWQiOlsicmVzMSJdLCJlc2VyX25hbMUiaiLlvKDkuIkiLCJzY29wZSI6WyJhbGwiXSwi¥XRpIjoiNTIwYj¥5Y2YtMjk3Mi00ZDQlLTlhZTQtODlhhlirf!xNjge¥jUyIiwiZXhwIjoxNTcwMDElODI2L CJhdXRob3Jpd611cyI6WyJwMSIsInAzI10sImp0aSI5IinJiO6Yir®CHLTZlYjEtNDQ3OCliYiinY2LTdinNGQ0ZjQl¥mIx/1yIsImNsaWVudF9pZCI6ImMxIn0.RledezCPxlsV-RGbicocIfin_cGmicJA -uTuLx_589Y1Q,j
“expires_in”: 7199,
6"scope”:-'all",
7"jti": ,520b69cf-6972-4d45-9ae4-89a6cl684b52"
6.3.4校验jwt令牌
资源服务需要和授权服务拥有一致的签字、令牌服务等:
1、 将授权服务中的TokenConfig类拷贝到资源服务中
2、 屏蔽资源服务原来的令牌服务类
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResouceServerConfig extends
ResourceServerConfigurerAdapter {
public static final String RESOURCE_ID = “resl”;
@Autowired
TokenStore tokenStore;
〃资源服务令牌解析服务
// @Bean
// public ResourceServerTokenServices tokenService() {
// 〃使用远倒艮务请求授权服务器校验token,必须指定校验token的u”、client_id , client_secret

RemoteTokenServices service=new RemoteTokenServices();
service.setCheckTokenEndpointUrl(“http://localhost:53020/uaa/oauth/check_token”); service.setClientId(“cl”);
service.setClientSecret(“secret”);
return service;
}
@0verride
public void configure(ResourceServerSecurityConfigurer resources) { resources.resourceld(RESOURCEID)
.tokenstore(tokenstore)
.stateless(true);
3、测试
1 )申请jwt令牌
2)使用令牌请求资源

小技巧:
令牌申请成功可以使用/uaa/oauth/check_token校验令牌的有效性,并查询令牌的内容,例子如下:

Value
Description
Pretty
Raw
Preview
JSON 7

“aud”:[
“resl”
L
“user name”:“张
,■scope-:[
:I “all”
“exp”: 1569763826, ,■authorities”:[
],
“jti”: “529b69cf-6972-4d45-9ae4-89a6cl684b52”, “cllent.id”: “cl”
6.4完善环境配置
截止目前客户端信息和授权码仍然存储在内存中,生产环境中通过会存储在数据库中,下边完善环境的配置:
6.4.1创建表
在user_db中创建如下表:
DROP TABLE IF EXISTS ‘oauth_client_details’;
CREATE TABLE ‘oauth_client_details’ (
‘client_id’ varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT ‘客户端标 识’

esource_ids’ varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT ‘接入资源列表’,
‘client_secret’ varchar(255) CHARACTER SET utf8
COMMENT,客户端秘钥
‘scope’ varchar(255) CHARACTER SET utf8 COLLATE
‘authorized_grant_types’ varchar(255) CHARACTER
NULL,
‘web_server_redirect_uri’ varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT
NULL,
‘authorities’ varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
‘access_token_validity’ int(ll) NULL DEFAULT NULL,
‘refresh_token_validity’ int(ll) NULL DEFAULT NULL,
‘additional_information’ longtext CHARACTER SET utf8 COLLATE utf8_general_ci NULL,
‘create_time’ timestamp(O) NOT NULL DEFAULT CURRENT_TIHESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0),
‘archived’ tinyint(4) NULL DEFAULT NULL,
‘trusted’ tinyint(4) NULL DEFAULT NULL,
‘autoapprove’ varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (‘client_id’) USING BTREE
)ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT =‘接入客户端信息’ ROW_FORMAT = Dynamic;
INSERT INTO ‘oauth_client_details’ VALUES (‘cl’, ‘resl’,
,$2a 10 10 10NlBC84MVb7F95EXYTXwLneXgCca6/GipyWR5NHm8K0203bSQMLpvm’, ‘ROLE_ADMIN,ROLE_USER,ROLE_API’, 'client_credentialSjpasswordauthorization_codeimplicitrefresh_token’3 ‘http://www.baidu.com’, NULL, 7200, 259200, NULL, ‘2019-09-09 16:04:28’, 0, 0, ‘false’);
INSERT INTO ‘oauth_client_details’ VALUES ('c2‘, ‘pes2‘,
,$2a 10 10 10NlBC84MVb7F95EXYTXwLneXgCca6/GipyWR5NHm8K0203bSQMLpvm’, ‘ROLE_API’,
'client_credentialSjpasswordauthorization_codeimplicitrefresh_token’3 ‘http://www.baidu.com’, NULL, 31536000, 2592000, NULL, ‘2019-09-09 21:48:51’, 0, 0, ‘false’);
oauth_code表,Spring Security 0Auth2使用,用来存储授权码:
DROP TABLE IF EXISTS ‘oauth_code’;
CREATE TABLE ‘oauth_code’ (
‘create_time’ timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP,
‘code’ varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
‘authentication’ blob NULL,
INDEX ‘code_index’(‘code’| USING BTREE
)ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
6.4.2配置授权服务
(1 )修改Authorizationserver :
ClientDetailsService 和 AuthorizationCodeServices 从数据库读取数据。
@Configuration
@EnableAuthorizationServer
public class Authorizationserver extends
AuthorizationServerConfigurerAdapter {
@Autowired
private TokenStore tokenstore;
@Autowired
private 3wtAccessTokenConventer accessTokenConverter;
@Autowired
private ClientDetailsService ClientDetailsService;
@Autowired
private AuthorizationCodeServices authorizationCodeServices;
@Autowired
private AuthenticationManager authenticationManager;
/**

  • 1.客户端详情相关配置
    /
    @Bean
    public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
    }
    @Bean
    public ClientDetailsService ClientDetailsService(DataSource dataSource) { ClientDetailsService ClientDetailsService = new Z)dbcClientDetailsService(dataSource); ((JdbcClientDetailsService)
    ClientDetailsService).setpasswordEncoder(passwordEncoder());
    return ClientDetailsService;
    }
    @Override public void configure(ClientDetailsServiceConfigurer clients)
    throws Exception {
    clients.withClientDetails(clientDetailsService);
    }
    /
    *
    2.配置令牌服务(token services)
    /
    @Bean
    public AuthorizationServerTokenServices tokenService() {
    DefaultTokenServices service=new DefaultTokenServices();
    service.setClientDetailsService(clientDetailsService);
    service. setSupportRefreshToken (true);//支持刷新令牌 service. setTokenStore(tokenStore); 〃绑定 token Store
    TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
    tokenEnhancerChain.setTokenEnhancers(Arrays.asList(accessTokenConverter)); service.setTokenEnhancer(tokenEnhancerChain);
    service.setAccessTokenValiditySeconds(7200); // 令牌默认有效期2小时
    service, set Ref reshTokenValiditySeconds( 259200); // 刷新令牌默认有效期 3 天 return service;
    }
    /

    3 .配置令牌(token )的访问端点
    /
    @Bean
    public AuthorizationCodeServices authorizationCodeServices(DataSource dataSource) { return new 3dbcAuthorizationCodeServices(dataSource);//设置授权码模式的授权码如何存取
    }
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
    endpoints.authenticationManager(authenticationManager)
    .authorizationCodeServices(authorizationCodeServices)
    .tokenServices(tokenService())
    .allowedTokenEndpointRequestMethods(HttpMethod.POST);
    }
    /

    *4.配置令牌端点(Token End point)的安全约束
    */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security)!
    security
    .tokenKeyAccess(“permitAll()”)
    .checkTokenAccess(“permitAll()”)
    .allowFormAuthenticationForClientsO//允许表单认证
    6.4.3测试
    1、 测试申请令牌
    使用密码模式申请令牌,客户端信息需要和数据库中的信息一致。
    2、 测试授权码模式
    生成的授权存储到数据库中

七、Spring Security实现分布式认证授权

7.1需求分析
回厕 支术方案如下:

1、 UAA认证服务负责认证授权。
2、 所有请求经过网关到达微服务
3、 网关负责鉴权客户端以及请求转发
4、 网关将token解析后传给微服务,微服务进行授权。
7.2.注册中心
所有微3艮务的请求都经过网关,网关从注册中心读取微务的地址,将请求转发至微务。
本节完成注册中心的搭建,注册中心采用Eureka。
1、创建maven工程
d.i s ti ibut ed^secuxi ty* di s c ovex y
B distributed_security-discovery. iml /TJ pom. xml
V src

java
com
itheima
security
distributed

f=resouxces
application, yml
test
2、pom.xm依赖如下

<?xml version="l .0" encoding="UTF-8,,?>



distributed-security
com.itheima.security
1.0-SNAPSHOT

4.0.0</model/ersion>
distributed-security-discovery


org.springframework.cloud
spring-cloud-starter-netflix-eureka-server


org.springframework.boot
spring-boot-starter-actuator


〈/project〉
3、配置文件
在 resource s 中酉己置 application.yml
spring:
application:
name: distributed-discovery
server:
port: 53000 #启动端口
eureka:
server:
enable-self-preservation: false #关闭服务器自我保护,客户端心跳检测15分与中内错误达到80%服务会保
护,导掰IJ人还认为是好用的服务
eviction-interval-timer-in-ms: 10000 #清理间隔(单位毫秒,默认是) 5秒将客户端剔除的服务在服 务注册列表中剔除#
shouldllseReadOnlyResponseCache: true #eureka是CAP理论种基于AP策略,为了保证强—致性关闭此切换CP 默认不关闭false关闭
client:
register-with-eureka: false 不作为一个客户端注册到注册中心
fetch-registry: false 日寸,可以启动,彳旦扌艮异常:Cannot execute request on any known
server
instance-info-replication-interval-seconds: 10
serviceUrl:
defaultzone: http://localhost:$(server.port}/eureka/
instance:
hostname: $(spring.cloud.client,ip-address}
prefer-ip-address: true
instance-id: s p r i n g . a p p l i c a t i o n ・ n a m e : {spring.application・name}: spring.applicationname:{spring.cloud.client・ip-
address): ( s p r i n g . a p p l i c a t i o n , i n s t a n c e i d : (spring.application , instance_id: (spring.application,instanceid:(server.port}}
启动类:
package com.itheima.security.distributed.discovery;
import org.springframework.boot.SpringApplication;
import org. springframework. boot. autoconfigure. SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class DiscoveryServer {
public static void main(String[] args) {
SpringApplication.run(DiscoveryServer.class^ args);
7.3.网关
网关整合OAuth2.0有两种思路,一种是认证服务器生成jwt令牌,所有请求统一在网关层验证,判断权限等操作; 另一种是由各资源服务处理,网关只做请求转发。
我们选用第一种。我们把API网关作为OAuth2.0的资源服务器角色,实现接入客户端权限拦截、令牌解析并转发当 前登录用户信息(js。nToken)给微服务,这样下游微服务就不需要关心令牌格式解析以及OAuth2.0相关机制了。
API网关在认证授权体系里主要负责两件事:
(1 ) {^OAuth2.0的资源服务器角色,实现接入方权限拦截。
(2 )令牌解析并转发当前登录用户信息(明文token )给糊艮务
微服务拿到明文token(明文token中包含登录用户的身份和权限信息)后也需要做两件事:
(1 )用户授权拦截(看当前用户是否有权访问该资源)
(2)将用户信息存储进当前线程上下文(有利于后续业务逻辑随时获取当前用户信息)
7.3.1创建工程
distxibuted^secuxity-gateway
B distributed-security-gateway, iml
/TJ pom. xml
V src
V main
v java
v Q com
v E itheima
v security
v distributed
v gateway

TH common
v 町 config
C Re s ouc e S erverC onf i g
C TokenConfig
C WebSecurityConfig
C ZuulConfig
El dto
El filter
翎 Gat ewayS er ver
V resources
application, properties
1、pom.xml

<?xml version="1.0" encoding="UTF-8,,?>



distributed-security
com.itheima.security
1.0-SNAPSHOT

4.0.0</model/ersion>
distributed-security-gateway


org.springframework.cloud
spring-cloud-starter-netflix-eureka-client


org.springframework.cloud spring-cloud-starter-netflix-hystrix


org.springframework.cloud
spring-cloud-starter-netflix-ribbon


org.springframework.cloud
spring-cloud-starter-openfeign


com.netflix.hystrix
hystrix-javanica


org.springframework.retry
spring-retry


org.springframework.boot
spring-boot-starter-actuator


org.springframework.boot spring-boot-starter-web


org.springframework.cloud
spring-cloud-starter-netflix-zuul


org.springframework.cloud spring-cloud-starter-security


org.springframework.cloud spring-cloud-starter-oauth2


org.springframework.security
spring-security-jwt

javax.interceptor javax.interceptor-api com.alibaba fastjson org.projectlombok lombok 2、配置文件 酉 Ma pplication.properties spring.application.name=gateway-server server.port=53010 spring.main.allow-bean-definition-overriding = true logging.level.root = info logging.level.org.springframework = info zuul.retryable = true zuul.ignoredServices = * zuul.add-host-header = true zuul.sensitiveHeaders = * zuul.routes.uaa-service.stripPrefix = false zuul.routes.uaa-service.path = /uaa/** zuul.routes.order-service.stripPrefix = false zuul.routes.order-service.path = /order/** eureka , client , servicellrl. defaultzone = http: //localhost: 53000/eureka/ eureka.instance.preferlpAddress = true eureka , instance.instance-id = $(spring.application ・ name}:$(spring.cloud.client ・ ip- address): $(spring.application , instance_id:$(server.port}} management.endpoints.web.exposure.include = refresh,health,info,env feign.hystrix.enabled = true feign.compression.request.enabled = true feign.compression.request.mime-types[0] = text/xml feign.compression.request.mime-types[1] = application/xml feign.compression.request.mime-types[2] = application/json feign.compression.request.min-request-size = 2048 feign.compression.response.enabled = true 统一认证服务(UAA )与统一用户服务都是网关下糊艮务,需要在网关上新增路由配置: zuul.routes.uaa-service.stripprefix = false zuul.routes.uaa-service.path = /uaa/** zuul.routes.user-service.stripprefix = false zuul.routes.user-service.path = /order/** 上面配置了网关接收的请求url若符合/order/**表达式,将被被转发至order-service(统一用户服务)。 启动类: @SpringBootApplication @EnableZuulProxy @EnableDiscoveryClient public class Gatewayserver { public static void main(String[] args) { SpringApplication.run(GatewayServer.class^ args); } } 7.3.2 token 配置 前面也介绍了 ,资源服务器由于需要验证并解析令牌,往往可以通过在授权服务器暴露check_token的Endpoint来 完成,而我们在授权服务器使用的是对称加密的jwt,因此知道密钥即可,资源服务与授权服务本就是对称设计, 那我们把授权服务的TokenConfig两个类拷贝过来就行。 package com.itheima.security.distributed.gateway.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.ZJwtAccessTokenConverter; import org.springframework.security.oauth2.provider.token.store.ZJwtTokenStore; @Configuration public class TokenConfig { private String SIGNING_KEY = "uaal23"; @Bean public TokenStore tokenStore() { return new JwtTokenStore(accessTokenConverter()); } @Bean public 3wtAccessTokenConverter accessTokenConverter() { 3wtAccessTokenConventer converter = new 3wtAccessTokenConverter(); converter.setSigningKey(SIGNING_KEY); 〃对腳!钥,资源服务器使用该秘钥来解密 return converter; } } 7.3.3配置资源服务 在ResouceServerConfig中定义资源服务配置,主要配置的内容就是定义一些匹配规则,描述某个接入客户端需要 什么样的权限才能访问某个糊艮务,如: @Configuration public class ResouceServerConfig { public static final String RESOURCE_ID = "resl"; /** *统证服务(UAA)资源拦截 */ @Configuration @EnableResourceServer public class UAAServerConfig extends ResourceServerConfigurerAdapter { @Autowired private TokenStore tokenstore; @Override public void configure(ResourceServerSecurityConfigurer resources)! resources.tokenStore(tokenStore).resourceId(RESOURCE_ID) .stateless(true); } @Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/uaa/**").permitAll(); } } /** *订单服务 */ @Configuration @EnableResourceServer public class OrderServerConfig extends ResourceServerConfigurerAdapter { @Autowired private TokenStore tokenstore; @Override public void configure(ResourceServerSecurityConfigurer resources) { resources.tokenStore(tokenStore).resourceId(RESOURCE_ID) .stateless(true); } @Override public void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/order/**").access("#oauth2.hasScope('ROLE_API'); } } } 上面定义了两个微^务的资源,其中: UAAServerConfig指定了若请求匹配/uaa/**网关不进行拦截。 OrderServerConfig指定了若请求匹配/order/** ,也就是访问统一用户服务,接入客户端需要有scope中包含 read ,并且authorities(权限)中需要包含ROLE_USERo 由于res1这个接入客户端,read包括ROLE_ADMIN,ROLE_USER,ROLE_API三个权限。 7.3.4安全配置 @Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { ^Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/**").permitAll() .and().csrf().disable(); } } 7.3.转发明文token给微服务 通过Zuul过滤器的方式实现,目的是让下游徴艮务能够很方便的获取到当前的登录用户信息(明文token ) (1 )实现Zuul前置过滤器,完成当前登录用户信息提取,并放入转发微服务的request中 /** * token传递拦截 */ public class AuthFilter extends ZuulFilter { @Override public boolean shouldFilter() { return true; } @Override public String filterType() { return "pre"; } @Override public int filterOrder() { return 0; } @Override public Object run() { /** *1.获取令牌内容 */ Requestcontext ctx = Requestcontext.getCurrentContext(); Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (! (authentication instanceof 0Auth2Authentication)){ // 无token访问网关内资源的情况,目 前仅有uu a服务直接暴露 return null; } 0Auth2Authentication oauth2Authentication = (0Auth2Authentication)authentication; Authentication userAuthentication = oauth2Authentication.getUserAuthentication(); Object principal = userAuthentication.getPrincipal(); /** *2.组装明文token ,转发给微服务,放入header ,名称为json-token */ List authorities = new ArrayList(); userAuthentication.getAuthorities().stream().forEach(s - >authorities.add(((GrantedAuthority) s).getAuthority())); 0Auth2Request oAuth2Request 二 oauth2Authentication.getOAuth2Request(); Map

传智播客旗下 高端IT教育品牌
(2 )将filter纳入spring 容器:
配置 Auth Filter
package com.itheima.security.distributed.gateway.config;
import com.itheima.security.distributed.gateway.filter.AuthFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationsource; import org.springframework.web.filter.CorsFilter;
^Configuration public class ZuulConfig {
@Bean
public AuthFilter preFileter() { return new AuthFilter();
}
@Bean
public FilterRegistrationBean corsFilter() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); final CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOrigin("");
config.addAllowedHeader("
");
config.addAllowedMethod("*");
config.setMaxAge(18000L);
source.registerCorsConfiguration('7**“config);
CorsFilter corsFilter = new CorsFilter(source);
FilterRegistrationBean bean = new FilterRegistrationBean(corsFilter);
bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
return bean;
}
}
7.4.微服务用户鉴权拦截
当微服务收到明文token时,应该怎么鉴权拦截呢?自己实现一个filter ?自己解析明文token ,自己定义一套资源 访问策略?
能不能适配Spring Security呢,是不是突然想起了前面我们实现的Spring Security基于to ken认证例子。咱们还拿 统一用户服务作为网关下游糊艮务,对它进行改造,增加微服务用户鉴权拦截功能。
(1)增加测试资源
OrderController 增加以下 endpoint
@PreAuthorize(“hasAuthority(‘pl’)”)
@GetMapping(value - “/rl”)
public String rl(){
UserDTO user = (UserDTO)
SecurityContextHolder.getContext().getAuthentication().getPrincipal();
return user.getUsername() + “访问资源 1”;
}
@PreAuthorize(“hasAuthority(‘p2’)”)
@GetMapping(value - “/r2”)
public String r2(){//通过Spring Security AP工获取当前登录用户
UserDTO user =
(UserDTO)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
return user.getUsername() + “访问资源2”;
}
(2)Spring Security 配置
开启方法保护,并增加Spring配置策略,除了/login方法不受保护(统一认证要调用),其他资源全部需要认证才能访 问。
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers(”/**").access("#oauth2.hasScope(‘ROLE_ADMIN’)") .and().csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
综合上面的配置,咱们共定义了三个资源了 ,拥有p1权限可以访问r1资源,拥有p2权限可以访问r2资源,只要认 证通过就能访问r3资源。
(3 )定义filter拦截token ,并开,成Spring Security的Authentication对象
@Component
public class TokenAuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest httpServletRequest^ HttpServletResponse httpServletResponseFilterchain filterchain) throws ServletExceptionlOException {
String token = httpServletRequest.getHeader(“json-token”);
if (token != null){
〃1.解析 token
String json = EncryptUtil.decodeUTF8StringBase64(token);
3SONObject userJson = 3SON.parseObject(json);
UserDTO user = new UserDTO();
user.setUsername(userJson.getString(“principal”));
3S0NArray authoritiesArray = userJson.get3S0NArray(“authorities”);
String [] authorities = authoritiesArray.toArray( new StringfauthoritiesArray.size()]);
//2.新建并填充 authentication
UsernamePasswordAuthenticationToken authentication = new
UsernamePasswordAuthenticationToken(
user, nullj AuthorityUtils.createAuthorityList(authorities));
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails( httpServletRequest));
〃3.将authentication保存进安全上下文
SecurityContextHolder.getContext().setAuthentication(authentication);
}
filterchain.doFilter(httpServletRequesthttpServletResponse);
}
}
经过上边的过虑器,资源服务中就可以方便到的获取用户的身份信息:
UserDTO user = (UserDTO) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
还是三个步骤:
1 .解析 token
2.新建并填充 authentication
3.将authentication保存进安全上下文 剩下的事儿就交给Spring Security好了。
7.5.集成测试
本案例测试过程描述:
1、 采用。Auth2.0的密码模式从UAA获取token
2、 使用该token通过网关访问订单服务的测试资源
(1 )过网关访问uaa的授权及获取令牌,获取token。注意端口是53010 ,网关的端口。
如授权endpoint :
http://localhost:53010/uaa/oauth/authorize?response_type=code&client_id=cl
令牌 endpoint
http://localhost:53010/uaa/oauth/token
(2 )使用Token过网关访问订单服务中的r1-r2测试资源进行测试。
结果:
使用张三t。ken访问p1 ,访问成功
使用张三t。ken访问p2 ,访问失败
使用李四token访问p1 ,访问失败
使用李四token访问p2 ,访问成功
符合预期结果。
(3 )破坏token测试
无to ken测试返回内容:
{
“error”: “unauthorized”,
,error_description": “Full authentication is required to access this resource”
}
破坏token测试返回内容:
(
“error”: “invalid_token”,
“error_description”: “Cannot convert access token to 3SON”
}
7.6扩展用户信息
7.6.1需求分析
目前wt令牌存储了用户的身份信息、权限信息,网关将token明文化转发给微^务使用,目前用户身份信息仅包括 了用户的账号,微^务还需要用户的ID、手机号等重要信息。
所以,本案例将提供扩展用户信息的思路和方法,满足微服务使用用户信息的需求。
下边分析JM令牌中扩展用户信息的方案:
在认证阶段DaoAuthenticationProvider会调用UserDetailService查询用户的信息,这里是可以获取至!I齐全的用户 信息的。由于JWT令牌中用户身份信息来源于UserDetails , UserDetails中仅定义了username为用户的身份信息, 这里有两个思路:第一是可以扩展UserDetails ,使之包括更多的自定义属性,第二也可以扩展username的内容
,比如存入json数据内容作为username的内容。相比较而言,方案二比较简单还不用破坏UserDetails的结构,我 们采用方案二
7.6.2 修改 UserDetailService
从数据库查询到user,将整体user转成json存入UserDetails对象。
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
〃登录略
System.out.printIn(“username=”+username);
〃根据账号去数据库查询…
UserDto user = userDao.getUserByUsername(username);
if(user == null)(
return null;
}
〃查询用户权限
List permissions = userDao.findPermissionsByUserId(user.getId());
Stringf] perarray = new String[permissions.size()]; permissions.toArray(perarray);
〃创建 UserDetails
〃这里将user转为json,将整体user存入use「Details
String principal = JSON.toUSONString(user);
UserDetails UserDetails =
User.withUsername(principal).password(user.getPassword()).authorities(perarray)・ build(); return UserDetails;
}
7.6.3修改资源服务过虑器
资源^务中的过虑器负责从header中解析json-token ,从中即可拿网关放入的用户身份信息,部分关键代码如 下:
if (token != null){
〃1.解析 token
String json = EncryptUtil.decodeUTF8StringBase64(token);
DSONObject userJson = 3SON.parseObject(json);
〃取出用户身份信息
String principal = userJson.getString(“principal”);
〃将json转成又摻
UserDTO userDTO = JSON.parseObject(principalj UserDTO.class);
DSONArray authoritiesArray = userJson.get3S0NArray(“authorities”); 以建程就完成自定义用户身份信息的方案。
8.课程总结
重点回顾:
什么是认证、授权、鶴
Java Servlet为支持http会话做了哪些事儿。
基于session认证机制的运作流程。
基于token认证机制的运作流程。
理解Spring Security的工作原理,Spring Securit瘗构总览,认证流程和授权,中间涉及到哪些组件,这些组件分 别处理什么,如何自定义这些组件满足个性需求。
OAuth2.0认证的四种模式?它们的大体流程是什么?
Spring cloud Security OAuth2包括哪些组件?职责?
分做系统认证需要解决的问题?
掌握学习方法,掌握思考方式。
附录
HttpSecurity
HttpSecurity配置列表:
传智播客旗下 高端IT教育品牌
方法 说明
openidLogin() 用于基于Openld的验证
headers() 将安全标头添加到响应
cors() 配置跨域资源共享(CORS )
sessionManagement() 允许配置会话管理
portMapper() 允许配置一 PortMapper(HttpSecurity#(getSharedObject(class))),其他提供SecurityConfigurer的对象使用 PortMapper 从 HTTP 重定 向到HTTPS或者从HTTPS重定向到HTTP。默认情况下,Spring Security使用fPortMapperlmpI映射HTTP端口8080到HTTPS端口 8443 , HTTP 端口80到 HTTPS 端口443
jee() 配置基于容器的预认证。在这种1®兄下,认证由Servlet容器管理
x509() 配置基于X509的认证
rememberMe 允许配置”记住我啲验证
authorizeRequests() 允许基于使用H ttp Servlet Re questPO访问
requestCache() 允许配置请求缓存
exceptionHandlingO 允许配置筒吴处理
securityContext() 在HttpServletRequests之间的SecurityContextHolder上设置Securitycontext的管理。当使用WebSecurityConfigurerAdapter时,这将 自动应用
servletApi() 将HttpServletRequest方法与在其上找到的值集成到SecurityContext中。当使用WebSecurityConfigurerAdapterflyf,这将自动应用
csrf() 添加 CSRF 支持,使用WebSecurityConfigurerAdapter时,默认启用
logout() 添加退出登录支持。当使用WebSecurityConfigurerAdapterflyf,这将自动应用。默认情况是,访问URL"/ logout”,使HTTP Session无效来 清除用户,清除已配置的任何#rememberMe。身份验证,清除SecurityContextHolder,然后重定向至旳login?success”
anonymous() 允许配置匿名用户的表示方法。当与WebSecurityConfigurerAdapte偌合使用时,这将自动应用。默认帽兄下,匿名用户将使用 org.springframework.security.authentication.AnonymousAuthenticationfbken^^x ,并包含角色"ROLE_ANONYMOUS”
formLogin() 指定支持基于表单的身份验证。如果未指定FormLoginConfigurer#loginPage(String),则将生成默认登录页面
oauth2Login() 根据夕陪ISOAuth 2.0或OpenlD Connect 1.0提供程序配置身份验证
requiresChannel() 配置通道安全。为了使该配置有用,必须提供至公«到所需信道的映射
httpBasic() 配置Http Basic验证
addFilterAt() 由旨定的Filter类的位置添加过滤器

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Shiro 是一个强大且易于使用的Java安全框架,提供了身份认证、授权、加密和会话管理等功能,可以用于构建安全稳定的分布式登录系统。 Shiro 分布式登录的架构可以通过集中式认证、授权服务来实现。在这个架构中,有一个独立的认证授权中心,所有需要登录的应用都将与该中心进行通信。 首先,用户在某个应用中输入用户名和密码进行登录。该应用将用户登录请求发送至认证授权中心。认证授权中心根据接收到的用户名和密码对用户进行认证,验证用户的身份是否合法。 如果认证成功,认证授权中心会生成一份包含用户身份、权限等信息的令牌(Token),并将该令牌返回给应用。应用可以将该令牌保存在客户端,用于后续的访问请求。 当用户访问其他需要登录权限的应用时,该应用会将用户的请求发送至认证授权中心进行令牌验证。认证授权中心会根据令牌中的信息判断用户的身份和权限是否满足要求。 如果验证通过,认证授权中心会给予应用相应的访问权限,用户可以成功访问该应用。如果验证不通过,用户将无法访问该应用。 通过这样的分布式登录架构,可以实现用户只需一次登录,就能访问多个应用的需求。这样的架构具有很好的安全性和可扩展性,适用于大规模分布式系统中。 总之,Shiro 分布式登录是一种通过集中式认证授权中心来实现用户统一登录,访问多个应用的安全方案。它能够提供稳定、安全且高效的分布式登录功能,具备广泛的应用价值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值