分布式 OAuth2.0认证授权(黑马讲义)()

  本篇文章为你整理了分布式 OAuth2.0认证授权(黑马讲义)()的详细内容,包含有 分布式 OAuth2.0认证授权(黑马讲义),希望能帮助你了解 分布式 OAuth2.0认证授权(黑马讲义)。

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

  
 

  分布式系统具体如下基本特点:

  分布性:每个部分都可以独立部署,服务之间交互通过网络进行通信,比如:订单服务、商品服务。
 

  2.伸缩性:每个部分都可以集群方式部署,并可针对部分结点进行硬件及软件扩容,具有一定的伸缩能力。
 

  3.共享性:每个部分都可以作为共享资源对外提供服务,多个部分可能有操作共享资源的情况。
 

  4.开放性:每个部分根据需求都可以对外发布共享资源的访问接口,并可允许第三方系统访问。

  
分布式系统的每个服务都会有认证、授权的需求,如果每个服务都实现一套认证授权逻辑会非常冗余,考虑分布式 系统共享性的特点,需要由独立的认证服务处理系统认证授权的请求;考虑分布式系统开放性的特点,不仅对系统 内部服务提供认证,对第三方系统也要提供认证。分布式认证的需求总结如下:
 

  统一认证授权
 

  提供独立的认证服务,统一处理认证授权。
 

  无论是不同类型的用户,还是不同种类的客户端(web端,H5、APP),均采用一致的认证、权限、会话机制,实现 统一认证授权。
 

  要实现统一则认证方式必须可扩展,支持各种认证需求,比如:用户名密码认证、短信验证码、二维码、人脸识别 等认证方式,并可以非常灵活的切换。
 

  应用接入认证
 

  应提供扩展和开放能力,提供安全的系统对接机制,并可开放部分API给接入第三方使用,一方应用(内部 系统服 务)和三方应用(第三方应用)均采用统一机制接入。

  1.3 分布式认证方案

  1.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带来额外的处理负担。
 

  1.3.2 技术方案

  根据 选型的分析,决定采用基于token的认证方式,它的优点是:
 

  1.适合统一认证的机制,客户端、一方应用、三方应用都遵循一致的认证机制。
 

  2.token认证方式对第三方应用接入更适合,因为它更开放,可使用当前有流行的开放协议Oauth2.0、JWT等。
 

  3.一般情况服务端无需存储会话信息,减轻了服务端的压力。

  分布式系统认证技术方案见下图:
 

  
 

  流程描述:
 

  (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网关方式的核心要点是,所有的接入方和消费端都通过统一的网关接入微服务,在网关层处理所 有的非业务功能。

  2 .OAuth2.0

  2.1 OAuth2.0介绍

  OAuth(开放授权)是一个开放标准,允许用户授权第三方应用访问他们存储在另外的服务提供者上的信息,而不 需要将用户名和密码提供给第三方应用或分享他们数据的所有内容。OAuth2.0是OAuth协议的延续版本,但不向 后兼容OAuth 1.0即完全废止了OAuth1.0。很多大公司如Google,Yahoo,Microsoft等都提供了OAUTH认证服 务,这些都足以说明OAUTH标准逐渐成为开放资源授权的标准。

  Oauth协议目前发展到2.0版本,1.0版本过于复杂,2.0版本已得到广泛应用。

  参考:https://baike.baidu.com/item/oAuth/7153134?fr=aladdin
 

  Oauth协议:https://tools.ietf.org/html/rfc6749

  下边分析一个Oauth2认证的例子,通过例子去理解OAuth2.0协议的认证流程,本例子是程序员网站使用微信 认证的过程,这个过程的简要描述如下:
 

  1、客户端请求第三方授权 用户进入程序的登录页面,点击微信的图标以微信账号登录系统,用户是自己在微信里信息的资源拥有者
 

  
 

  点击“微信”出现一个二维码,此时用户扫描二维码,开始给网站授权。
 

  
 

  2、资源拥有者同意给客户端授权
 

  资源拥有者扫描二维码表示资源拥有者同意给客户端授权,微信会对资源拥有者的身份进行验证, 验证通过后,微 信会询问用户是否给授权网站访问自己的微信数据,用户点击“确认登录”表示同意授权,微信认证服务器会 颁发一个授权码,并重定向到网站。
 

  
 

  3、客户端获取到授权码,请求认证服务器申请令牌 此过程用户看不到,客户端应用程序请求认证服务器,请求携带授权码。
 

  4、认证服务器向客户端响应令牌 微信认证服务器验证了客户端请求的授权码,如果合法则给客户端颁发令牌,令牌是客户端访问资源的通行证。 此交互过程用户看不到,当客户端拿到令牌后,用户在黑马程序员看到已经登录成功。
 

  5、客户端请求资源服务器的资源 客户端携带令牌访问资源服务器的资源。 黑马程序员网站携带令牌请求访问微信服务器获取用户的基本信息。
 

  6、资源服务器返回受保护资源 资源服务器校验令牌的合法性,如果合法则向用户响应资源信息内容。

  以上认证授权详细的执行流程如下:
 

  
 

  通过上边的例子我们大概了解了OAauth2.0的认证过程,下边我们看OAuth2.0认证流程:
 

  引自OAauth2.0协议rfc6749 https://tools.ietf.org/html/rfc6749
 

  
 

  OAauth2.0包括以下角色:
 

  1、客户端
 

  本身不存储资源,需要通过资源拥有者的授权去请求资源服务器的资源,比如:Android客户端、Web客户端(浏 览器端)、微信客户端等。
 

  2、资源拥有者
 

  通常为用户,也可以是应用程序,即该资源的拥有者。
 

  3、授权服务器(也称认证服务器)
 

  用于服务提供商对资源拥有的身份进行认证、对访问资源进行授权,认证成功后会给客户端发放令牌 (access_token),作为客户端访问资源服务器的凭据。本例为微信的认证服务器。
 

  4、资源服务器
 

  存储资源的服务器,本例子为微信存储的用户信息。

  现在还有一个问题,服务提供商能允许随便一个客户端就接入到它的授权服务器吗?答案是否定的,服务提供商会 给准入的接入方一个身份,用于接入时的凭据:
 

  client_id:客户端标识
 

  client_secret:客户端秘钥
 

  因此,准确来说,授权服务器对两种OAuth2.0中的两个角色进行认证授权,分别是资源拥有者、客户端。

  2.2 Spring Cloud Security OAuth2

  2.2.1 环境介绍

  Spring-Security-OAuth2是对OAuth2的一种实现,并且跟我们之前学习的Spring Security相辅相成,与Spring Cloud体系的集成也非常便利,接下来,我们需要对它进行学习,最终使用它来实现我们设计的分布式认证授权解决方案。

  OAuth2.0的服务提供方涵盖两个服务,即授权服务 (Authorization Server,也叫认证服务) 和资源服务 (Resource Server),使用 Spring Security OAuth2 的时候你可以选择把它们在同一个应用程序中实现,也可以选择建立使用 同一个授权服务的多个资源服务。

  授权服务 (Authorization Server)应包含对接入端以及登入用户的合法性进行验证并颁发token等功能,对令牌 的请求端点由 Spring MVC 控制器进行实现,下面是配置一个认证服务必须要实现的endpoints:

  AuthorizationEndpoint 服务于认证请求。默认 URL: /oauth/authorize 。

  TokenEndpoint 服务于访问令牌的请求。默认 URL: /oauth/token 。 资源服务 (Resource Server),应包含对资源的保护功能,对非法请求进行拦截,对请求中token进行解析鉴 权等,下面的过滤器用于实现 OAuth 2.0 资源服务

  OAuth2AuthenticationProcessingFilter用来对请求给出的身份令牌解析鉴权。

  分别创建uaa授权服务(也可叫认证服务)和order订单资源服务。
 

  
 

  认证流程如下:
 

  1、客户端请求UAA授权服务进行认证。
 

  2、认证通过后由UAA颁发令牌。
 

  3、客户端携带令牌Token请求资源服务。
 

  4、资源服务校验令牌的合法性,合法即返回资源信息。

  2.2.2 环境搭建

  2.2.2.1 父工程

  创建maven工程作为父工程,依赖如下:

  

 dependency 

 

   groupId org.springframework.cloud /groupId

   artifactId spring-cloud-dependencies /artifactId

   version Greenwich.RELEASE /version

   type pom /type

   scope import /scope

   /dependency

  
groupId org.springframework.security /groupId

   artifactId spring-security-jwt /artifactId

   version 1.0.10.RELEASE /version

   /dependency

  
groupId org.springframework.security.oauth.boot /groupId

   artifactId spring-security-oauth2-autoconfigure /artifactId

   version 2.1.3.RELEASE /version

   /dependency

  

 

  2.2.2.2 创建UAA授权服务工程

  1、创建distributed-security-uaa
 

  创建distributed-security-uaa作为授权服务工程,依赖如下:

  

 dependency 

 

   groupId org.springframework.cloud /groupId

   artifactId spring-cloud-starter-netflix-eureka-client /artifactId

   /dependency

   dependency

   groupId org.springframework.cloud /groupId

   artifactId spring-cloud-starter-netflix-hystrix /artifactId

   /dependency

   dependency

   groupId org.springframework.cloud /groupId

   artifactId spring-cloud-starter-netflix-ribbon /artifactId

   /dependency

   dependency

   groupId org.springframework.cloud /groupId

   artifactId spring-cloud-starter-openfeign /artifactId

   /dependency

   dependency

   groupId com.netflix.hystrix /groupId

   artifactId hystrix-javanica /artifactId

   /dependency

   dependency

   groupId org.springframework.retry /groupId

   artifactId spring-retry /artifactId

   /dependency

   dependency

   groupId org.springframework.boot /groupId

   artifactId spring-boot-starter-actuator /artifactId

   /dependency

  
groupId org.springframework.boot /groupId

   artifactId spring-boot-starter-web /artifactId

   /dependency

   dependency

   groupId org.springframework.boot /groupId

   artifactId spring-boot-starter-freemarker /artifactId

   /dependency

  
groupId org.springframework.data /groupId

   artifactId spring-data-commons /artifactId

   /dependency

   dependency

   groupId org.springframework.cloud /groupId

   artifactId spring-cloud-starter-security /artifactId

   /dependency

   dependency

   groupId org.springframework.cloud /groupId

   artifactId spring-cloud-starter-oauth2 /artifactId

   /dependency

   dependency

   groupId org.springframework.security /groupId

   artifactId spring-security-jwt /artifactId

   /dependency

   dependency

   groupId javax.interceptor /groupId

   artifactId javax.interceptor-api /artifactId

   /dependency

   dependency

   groupId mysql /groupId

   artifactId mysql-connector-java /artifactId

   /dependency

   dependency

   groupId org.springframework.boot /groupId

   artifactId spring-boot-starter-jdbc /artifactId

   /dependency

  
@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.serviceUrl.defaultZone = http://localhost:53000/eureka/

  eureka.instance.preferIpAddress = 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

  

 

  2.2.2.3 创建Order资源服务工程

  本工程为Order订单服务工程,访问本工程的资源需要认证通过。
 

  本工程的目的主要是测试认证授权的功能,所以不涉及订单管理相关业务。
 

  创建Order工程

  

 dependency 

 

   groupId org.springframework.cloud /groupId

   artifactId spring-cloud-starter-netflix-eureka-client /artifactId

   /dependency

   dependency

   groupId org.springframework.boot /groupId

   artifactId spring-boot-starter-actuator /artifactId

   /dependency

   dependency

   groupId org.springframework.boot /groupId

   artifactId spring-boot-starter-web /artifactId

   /dependency

   dependency

   groupId org.springframework.cloud /groupId

   artifactId spring-cloud-starter-security /artifactId

   /dependency

   dependency

   groupId org.springframework.cloud /groupId

   artifactId spring-cloud-starter-oauth2 /artifactId

   /dependency

   dependency

   groupId javax.interceptor /groupId

   artifactId javax.interceptor-api /artifactId

   /dependency

   dependency

   groupId com.alibaba /groupId

   artifactId fastjson /artifactId

   /dependency

   dependency

   groupId org.projectlombok /groupId

   artifactId lombok /artifactId

   /dependency

  

 

  配置文件
 

  在resources中创建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.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 = ${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

  

 

  启动类

  

@SpringBootApplication

 

  @EnableDiscoveryClient

  public class OrderServer {

   public static void main(String[] args) {

   SpringApplication.run(OrderServer.class, args);

  

 

  2.2.2.授权服务器配置

  2.2.2.1 EnableAuthorizationServer

  可以用 @EnableAuthorizationServer 注解并继承AuthorizationServerConfigurerAdapter来配置OAuth2.0 授权 服务器。
 

  在Config包下创建AuthorizationServer:

  

@Configuration

 

  @EnableAuthorizationServer

  public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {

   @Autowired

   private TokenStore tokenStore;

   @Autowired

   private ClientDetailsService clientDetailsService;

   @Autowired

   private AuthorizationCodeServices authorizationCodeServices;

   @Autowired

   private AuthenticationManager authenticationManager;

   @Autowired

   private JwtAccessTokenConverter accessTokenConverter;

   @Autowired

   PasswordEncoder passwordEncoder;

   //将客户端信息存储到数据库

   @Bean

   public ClientDetailsService clientDetailsService(DataSource dataSource) {

   ClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);

   ((JdbcClientDetailsService) clientDetailsService).setPasswordEncoder(passwordEncoder);

   return clientDetailsService;

   //客户端详情服务

   @Override

   public void configure(ClientDetailsServiceConfigurer clients)

   throws Exception {

   clients.withClientDetails(clientDetailsService);

   /* clients.inMemory()// 使用in-memory存储

   .withClient("c1")// client_id

   .secret(new BCryptPasswordEncoder().encode("secret"))//客户端密钥

   .resourceIds("res1")//资源列表

   .authorizedGrantTypes("authorization_code", "password","client_credentials","implicit","refresh_token")// 该client允许的授权类型authorization_code,password,refresh_token,implicit,client_credentials

   .scopes("all")// 允许的授权范围

   .autoApprove(false)//false跳转到授权页面

   //加上验证回调地址

   .redirectUris("http://www.baidu.com")*/

  
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;

   //设置授权码模式的授权码如何存取,暂时采用内存方式

  /* @Bean

   public AuthorizationCodeServices authorizationCodeServices() {

   return new InMemoryAuthorizationCodeServices();

   @Bean

   public AuthorizationCodeServices authorizationCodeServices(DataSource dataSource) {

   return new JdbcAuthorizationCodeServices(dataSource);//设置授权码模式的授权码如何存取

   @Override

   public void configure(AuthorizationServerEndpointsConfigurer endpoints) {

   endpoints

   .authenticationManager(authenticationManager)//认证管理器

   .authorizationCodeServices(authorizationCodeServices)//授权码服务

   .tokenServices(tokenService())//令牌管理服务

   .allowedTokenEndpointRequestMethods(HttpMethod.POST);

   @Override

   public void configure(AuthorizationServerSecurityConfigurer security){

   security

   .tokenKeyAccess("permitAll()") //oauth/token_key是公开

   .checkTokenAccess("permitAll()") //oauth/check_token公开

   .allowFormAuthenticationForClients() //表单认证(申请令牌)

  

 

  AuthorizationServerConfigurerAdapter要求配置以下几个类,这几个类是由Spring创建的独立的配置对象,它们 会被Spring传入AuthorizationServerConfigurer中进行配置。
 

  ClientDetailsServiceConfigurer:用来配置客户端详情服务(ClientDetailsService),客户端详情信息在 这里进行初始化,你能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息。

  AuthorizationServerEndpointsConfigurer:用来配置令牌(token)的访问端点和令牌服务(token services)。

  AuthorizationServerSecurityConfigurer:用来配置令牌端点的安全约束.

  2.2.2.1.配置客户端详细信息

  ClientDetailsServiceConfigurer 能够使用内存或者JDBC来实现客户端详情服务(ClientDetailsService), ClientDetailsService负责查找ClientDetails,而ClientDetails有几个重要的属性如下列表:

  clientId:(必须的)用来标识客户的Id。

  secret:(需要值得信任的客户端)客户端安全码,如果有的话。

  scope:用来限制客户端的访问范围,如果为空(默认)的话,那么客户端拥有全部的访问范围。

  authorizedGrantTypes:此客户端可以使用的授权类型,默认为空。

  authorities:此客户端可以使用的权限(基于Spring Security authorities)。

  客户端详情(Client Details)能够在应用程序运行的时候进行更新,可以通过访问底层的存储服务(例如将客户 端详情存储在一个关系数据库的表中,就可以使用 JdbcClientDetailsService)或者通过自己实现 ClientRegistrationService接口(同时你也可以实现 ClientDetailsService 接口)来进行管理。

  我们暂时使用内存方式存储客户端详情信息,配置如下
 

  2.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 所扮演的角色是一样的。

  定义TokenConfig
 

  在config包下定义TokenConfig,我们暂时先使用InMemoryTokenStore,生成一个普通的令牌
 

  
 

  代码:

  

@Configuration

 

  public class TokenConfig {

   private String SIGNING_KEY = "uaa123";

   @Bean

   public TokenStore tokenStore() {

   //JWT令牌存储方案

   return new JwtTokenStore(accessTokenConverter());

   @Bean

   public JwtAccessTokenConverter accessTokenConverter() {

   JwtAccessTokenConverter converter = new JwtAccessTokenConverter();

   converter.setSigningKey(SIGNING_KEY); //对称秘钥,资源服务器使用该秘钥来验证

   return converter;

   /* @Bean

   public TokenStore tokenStore() {

   //使用内存存储令牌(普通令牌)

   return new InMemoryTokenStore();

  

 

  定义AuthorizationServerTokenServices
 

  在AuthorizationServer中定义AuthorizationServerTokenServices
 

  2.2.2.3.令牌访问端点配置

  AuthorizationServerEndpointsConfigurer 这个对象的实例可以完成令牌服务以及令牌endpoint配置。
 

  配置授权类型(Grant Types)
 

  AuthorizationServerEndpointsConfigurer 通过设定以下属性决定支持的授权类型(Grant Types):

  authenticationManager:认证管理器,当你选择了资源所有者密码(password)授权类型的时候,请设置 这个属性注入一个 AuthenticationManager 对象。

  userDetailsService:如果你设置了这个属性的话,那说明你有一个自己的 UserDetailsService 接口的实现, 或者你可以把这个东西设置到全局域上面去(例如 GlobalAuthenticationManagerConfigurer 这个配置对 象),当你设置了这个之后,那么 "refresh_token" 即刷新令牌授权类型模式的流程中就会包含一个检查,用 来确保这个账号是否仍然有效,假如说你禁用了这个账户的话。

  authorizationCodeServices:这个属性是用来设置授权码服务的(即 AuthorizationCodeServices 的实例对 象),主要用于 "authorization_code" 授权码类型模式。

  implicitGrantService:这个属性用于设置隐式授权模式,用来管理隐式授权模式的状态。

  tokenGranter:当你设置了这个东西(即 TokenGranter 接口实现),那么授权将会交由你来完全掌控,并 且会忽略掉上面的这几个属性,这个属性一般是用作拓展用途的,即标准的四种授权模式已经满足不了你的 需求的时候,才会考虑使用这个。

  配置授权端点的URL(Endpoint URLs):
 

  AuthorizationServerEndpointsConfigurer 这个配置对象有一个叫做 pathMapping() 的方法用来配置端点URL链 接,它有两个参数:

  第一个参数:String 类型的,这个端点URL的默认链接。

  第二个参数:String 类型的,你要进行替代的URL链接。

  以上的参数都将以 "/" 字符为开始的字符串,框架的默认URL链接如下列表,可以作为这个 pathMapping() 方法的 第一个参数:

  /oauth/authorize:授权端点。

  /oauth/token:令牌端点。

  /oauth/confirm_access:用户确认授权提交端点。

  /oauth/error:授权服务错误信息端点。

  /oauth/check_token:用于资源服务访问的令牌解析端点。

  /oauth/token_key:提供公有密匙的端点,如果你使用JWT令牌的话。

  需要注意的是授权端点这个URL应该被Spring Security保护起来只供授权用户访问.

  在AuthorizationServer配置令牌访问端点
 

  2.2.2.4.令牌端点的安全约束

  AuthorizationServerSecurityConfigurer:用来配置令牌端点(Token Endpoint)的安全约束,在 AuthorizationServer中配置如下
 

  
 

  (1)tokenkey这个endpoint当使用JwtToken且使用非对称加密时,资源服务用于获取公钥而开放的,这里指这个 endpoint完全公开。
 

  (2)checkToken这个endpoint完全公开
 

  (3) 允许表单认证

  授权服务配置总结:授权服务配置分成三大块,可以关联记忆。

  既然要完成认证,它首先得知道客户端信息从哪儿读取,因此要进行客户端详情配置。

  既然要颁发token,那必须得定义token的相关endpoint,以及token如何存取,以及客户端支持哪些类型的 token。

  既然暴露除了一些endpoint,那对这些endpoint可以定义一些安全上的约束等。

  2.2.2.5 web安全配置

  

@Configuration

 

  @EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)

  public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

   //认证管理器

   @Bean

   public AuthenticationManager authenticationManagerBean() throws Exception {

   return super.authenticationManagerBean();

   //密码编码器

   @Bean

   public PasswordEncoder passwordEncoder() {

   return new BCryptPasswordEncoder();

   //安全拦截机制(最重要)

   @Override

   protected void configure(HttpSecurity http) throws Exception {

   http.csrf().disable()

   .authorizeRequests()

   .antMatchers("/r/r1").hasAnyAuthority("p1")

   .antMatchers("/login*").permitAll()

   .anyRequest().authenticated()

   .and()

   .formLogin()

  

 

  2.2.3.授权码模式

  2.2.3.1 授权码模式介绍

  下图是授权码模式交互图
 

  
 

  (1)资源拥有者打开客户端,客户端要求资源拥有者给予授权,它将浏览器被重定向到授权服务器,重定向时会 附加客户端的身份信息。如:

  

/uaa/oauth/authorize?client_id=c1 response_type=code scope=all redirect_uri=http://www.baidu.com

 

  

 

  参数列表如下:

  client_id:客户端准入标识。

  response_type:授权码模式固定为code。

  scope:客户端权限。

  redirect_uri:跳转uri,当授权码申请成功后会跳转到此地址,并在后边带上code参数(授权码)。

  (2)浏览器出现向授权服务器授权页面,之后将用户同意授权。
 

  (3)授权服务器将授权码(AuthorizationCode)转经浏览器发送给client(通过redirect_uri)。
 

  (4)客户端拿着授权码向授权服务器索要访问access_token,请求如下:

  

/uaa/oauth/token? client_id=c1 client_secret=secret grant_type=authorization_code code=5PgfcD redirect_uri=http://www.baidu.com

 

  

 

  参数列表如下

  client_id:客户端准入标识。

  client_secret:客户端秘钥。

  grant_type:授权类型,填写authorization_code,表示授权码模式

  code:授权码,就是刚刚获取的授权码,注意:授权码只使用一次就无效了,需要重新申请。

  redirect_uri:申请授权码时的跳转url,一定和申请授权码时用的redirect_uri一致。

  (5)授权服务器返回令牌(access_token)
 

  这种模式是四种模式中最安全的一种模式。一般用于client是Web服务器端应用或第三方的原生App调用资源服务 的时候。因为在这种模式中access_token不会经过浏览器或移动端的App,而是直接从服务端去交换,这样就最大 限度的减小了令牌泄漏的风险。

  2.2.3.2 测试

  浏览器访问认证页面:
 

  http://localhost:53020/uaa/oauth/authorize? client_id=c1 response_type=code scope=all redirect_uri=http://www.baidu.com
 

  然后输入模拟的账号和密码点登陆之后进入授权页面:
 

  
 

  确认授权后,浏览器会重定向到指定路径(oauth_client_details表中的web_server_redirect_uri)并附加验证码? code=DB2mFj(每次不一样),最后使用该验证码获取token。
 

  POST请求 http://localhost:53020/uaa/oauth/token 参数的值配置好
 

  2.2.4.简化模式

  2.2.4.1 简化模式介绍

  下图是简化模式交互图:
 

  
 

  (1)资源拥有者打开客户端,客户端要求资源拥有者给予授权,它将浏览器被重定向到授权服务器,重定向时会 附加客户端的身份信息。如:

  

/uaa/oauth/authorize?client_id=c1 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 的值。如https://domain/index#L18这个 URI 中 L18 就是 fragment 的值。大家只需要 知道js通过响应浏览器地址栏变化的方式能获取到fragment 就行了。

  一般来说,简化模式用于没有服务器端的第三方单页面应用,因为没有服务器端就无法接收授权码。

  2.2.4.2 测试

  浏览器访问认证页面:
 

  http://localhost:53020/uaa/oauth/authorize?client_id=c1 response_type=token scope=all redirect_uri=http://www.baidu.com

  确认授权后,浏览器会重定向到指定路径(oauth_client_details表中的web_server_redirect_uri)并以Hash的形 式存放在重定向uri的fargment中,如:
 

  2.2.5.密码模式

  2.2.5.1 授权码模式介绍

  (1)资源拥有者将用户名、密码发送给客户端
 

  (2)客户端拿着资源拥有者的用户名、密码向授权服务器请求令牌(access_token),请求如下:

  

/uaa/oauth/token? client_id=c1 client_secret=secret grant_type=password username=shangsan password=123

 

  

 

  参数列表如下:
 

  client_id:客户端准入标识。
 

  client_secret:客户端秘钥。
 

  grant_type:授权类型,填写password表示密码模式
 

  username:资源拥有者用户名。
 

  password:资源拥有者密码。

  (3)授权服务器将令牌(access_token)发送给client
 

  这种模式十分简单,但是却意味着直接将用户敏感信息泄漏给了client,因此这就说明这种模式只能用于client是我 们自己开发的情况下。因此密码模式一般用于我们自己开发的,第一方原生App或第一方单页面应用。

  2.2.5.2 测试

  POST请求 http://localhost:53020/uaa/oauth/token 加上那几个参数
 

  
 

  返回结果:
 

  2.2.6.客户端模式

  2.2.6.1 客户端模式介绍

  (1)客户端向授权服务器发送自己的身份信息,并请求令牌(access_token)
 

  (2)确认客户端身份无误后,将令牌(access_token)发送给client,请求如下:

  

/uaa/oauth/token?client_id=c1 client_secret=secret grant_type=client_credentials

 

  

 

  参数列表如下:
 

  client_id:客户端准入标识。
 

  client_secret:客户端秘钥。
 

  grant_type:授权类型,填写client_credentials表示客户端模式
 

  这种模式是最方便但最不安全的模式。因此这就要求我们对client完全的信任,而client本身也是安全的。因 此这种模式一般用来提供给我们完全信任的服务器端服务。比如,合作方系统对接,拉取一组用户信息。

  2.2.6.2 客户端模式测试

  POST http://localhost:53020/uaa/oauth/token 加上参数
 

  2.2.7.资源服务测试

  2.2.7.1 资源服务器配置

  @EnableResourceServer 注解到一个 @Configuration 配置类上,并且必须使用 ResourceServerConfigurer 这个 配置对象来进行配置(可以选择继承自 ResourceServerConfigurerAdapter 然后覆写其中的方法,参数就是这个 对象的实例),下面是一些可以配置的属性:
 

  ResourceServerSecurityConfigurer中主要包括:

  tokenServices:ResourceServerTokenServices 类的实例,用来实现令牌服务。

  tokenStore:TokenStore类的实例,指定令牌如何访问,与tokenServices配置可选

  resourceId:这个资源服务的ID,这个属性是可选的,但是推荐设置并在授权服务中进行验证。

  其他的拓展属性例如 tokenExtractor 令牌提取器用来提取请求中的令牌。

  HttpSecurity配置这个与Spring Security类似:

  请求匹配器,用来设置需要进行保护的资源路径,默认的情况下是保护资源服务的全部路径。

  通过http.authorizeRequests()来设置受保护资源的访问规则

  其他的自定义权限保护规则通过 HttpSecurity 来进行配置。

  @EnableResourceServer 注解自动增加了一个类型为 OAuth2AuthenticationProcessingFilter 的过滤器链
 

  编写ResouceServerConfig:

  

@Configuration

 

  @EnableResourceServer

  public class ResouceServerConfig extends ResourceServerConfigurerAdapter {

  
public void configure(ResourceServerSecurityConfigurer resources) {

   resources.resourceId(RESOURCE_ID)//资源 id

   .tokenStore(tokenStore)

  // .tokenServices(tokenService())//验证令牌的服务

   .stateless(true);

   @Override

   public void configure(HttpSecurity http) throws Exception {

   http

   .authorizeRequests()

   .antMatchers("/**").access("#oauth2.hasScope(all)")

   .and().csrf().disable()

   .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

  

 

  2.2.7.2 验证token

  ResourceServerTokenServices 是组成授权服务的另一半,如果你的授权服务和资源服务在同一个应用程序上的 话,你可以使用 DefaultTokenServices ,这样的话,你就不用考虑关于实现所有必要的接口的一致性问题。如果 你的资源服务器是分离开的,那么你就必须要确保能够有匹配授权服务提供的 ResourceServerTokenServices,它 知道如何对令牌进行解码。

  令牌解析方法: 使用 DefaultTokenServices 在资源服务器本地配置令牌存储、解码、解析方式 使用 RemoteTokenServices 资源服务器通过 HTTP 请求来解码令牌,每次都请求授权服务器端点 /oauth/check_token

  使用授权服务的 /oauth/check_token 端点你需要在授权服务将这个端点暴露出去,以便资源服务可以进行访问, 这在咱们授权服务配置中已经提到了,下面是一个例子,在这个例子。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: