springsecurity02-springsecurity oauth2实现单点登录之-授权服务器(Authorization Service)

oauth2简介

具体关于oauth2,请参考https://blog.csdn.net/liaomin416100569/article/details/78871969
简要介绍一下使用到的概念和术语
(1) Third-party application:第三方应用程序,本文中又称"客户端"(client)。
(2)HTTP service:HTTP服务提供商,本文中简称"服务提供商"。
(3)Resource Owner:资源所有者,本文中又称"用户"(user)。
(4)User Agent:用户代理,本文中就是指浏览器。
(5)Authorization server:认证服务器,即服务提供商专门用来处理认证的服务器。
(6)Resource server:资源服务器,即服务提供商存放用户生成的资源的服务器。它与认证服务器,可以是同一台服务器,也可以是不同的服务器。
OAuth 2.0的运行流程如下图
在这里插入图片描述
过程
(A)用户打开客户端以后,客户端要求用户给予授权。
(B)用户同意给予客户端授权。
(C)客户端使用上一步获得的授权,向认证服务器申请令牌。
(D)认证服务器对客户端进行认证以后,确认无误,同意发放令牌。
(E)客户端使用令牌,向资源服务器申请获取资源。
(F)资源服务器确认令牌无误,同意向客户端开放资源。

客户端必须得到用户的授权(authorization grant),才能获得令牌(access token)。OAuth 2.0定义了四种授权方式。

  • 授权码模式(authorization code)
  • 简化模式(implicit)
  • 密码模式(resource owner password credentials)
  • 客户端模式(client credentials)

常用的是授权码模式
在这里插入图片描述
它的步骤如下:
(A)用户访问客户端,后者将前者导向认证服务器。
(B)用户选择是否给予客户端授权。
(C)假设用户给予授权,认证服务器将用户导向客户端事先指定的"重定向URI"(redirection URI),同时附上一个授权码。
(D)客户端收到授权码,附上早先的"重定向URI",向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。
(E)认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。
下面是上面这些步骤所需要的参数。

A步骤中,客户端申请认证的URI,包含以下参数:

  • response_type:表示授权类型,必选项,此处的值固定为"code"
  • client_id:表示客户端的ID,必选项
  • redirect_uri:表示重定向URI,可选项
  • scope:表示申请的权限范围,可选项
  • state:表示客户端的当前状态,可以指定任意值,认证服务器会原封不动地返回这个值。
    下面是一个例子。
GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz
        &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com

C步骤中,服务器回应客户端的URI,包含以下参数:

  • code:表示授权码,必选项。该码的有效期应该很短,通常设为10分钟,客户端只能使用该码一次,否则会被授权服务器拒绝。该码与客户端ID和重定向URI,是一一对应关系。
  • state:如果客户端的请求中包含这个参数,认证服务器的回应也必须一模一样包含这个参数。
    下面是一个例子。
 
HTTP/1.1 302 Found
Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA
          &state=xyz

D步骤中,客户端向认证服务器申请令牌的HTTP请求,包含以下参数:

  • grant_type:表示使用的授权模式,必选项,此处的值固定为"authorization_code"。
  • code:表示上一步获得的授权码,必选项。
  • redirect_uri:表示重定向URI,必选项,且必须与A步骤中的该参数值保持一致。
  • client_id:表示客户端ID,必选项。
    下面是一个例子。
POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb

E步骤中,认证服务器发送的HTTP回复,包含以下参数:

  • access_token:表示访问令牌,必选项。

  • token_type:表示令牌类型,该值大小写不敏感,必选项,可以是bearer类型或mac类型。
    expires_in:表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。
    refresh_token:表示更新令牌,用来获取下一次的访问令牌,可选项。
    scope:表示权限范围,如果与客户端申请的范围一致,此项可省略。
    下面是一个例子。

    HTTP/1.1 200 OK
    Content-Type: application/json;charset=UTF-8
    Cache-Control: no-store
    Pragma: no-cache

    {
    “access_token”:“2YotnFZFEjr1zCsicMWpAA”,
    “token_type”:“example”,
    “expires_in”:3600,
    “refresh_token”:“tGzv3JOkF0XG5Qx2TlKWIA”,
    “example_parameter”:“example_value”
    }

从上面代码可以看到,相关参数使用JSON格式发送(Content-Type: application/json)。此外,HTTP头信息中明确指定不得缓存。
springsecurity oauth2文档向导 https://projects.spring.io/spring-security-oauth/docs/oauth2.html

springboot使用springsecurity完成oauth2

整个工程包括三个独立的应用,一个认证服务和两个客户端应用,结构非常简单。当一个用户访问客户端应用中被防护的API时,系统会被自动重定向到认证服务,之后我们使用OAuth2.0的Authorization code授权方式来实现认证授权。

授权服务

授权服务器配置指南

英语文章介绍官网:https://projects.spring.io/spring-security-oauth/docs/oauth2.html
Spring OAuth2.0提供者实际上分为:

  • 授权服务 Authorization Service.
  • 资源服务 Resource Service.

虽然这两个提供者有时候可能存在同一个应用程序中,但在Spring Security OAuth中你可以把
他它们各自放在不同的应用上,而且你可以有多个资源服务,它们共享同一个中央授权服
务。

所有获取令牌的请求都将会在Spring MVC controller endpoints中进行处理,并且访问受保护
的资源服务的处理流程将会放在标准的Spring Security请求过滤器中(filters)。

下面是配置一个授权服务必须要实现的endpoints(就是控制层,有@RequstMapping注解):

  • AuthorizationEndpoint:用来作为请求者获得授权的服务,默认的URL是/oauth/authorize.
  • TokenEndpoint:用来作为请求者获得令牌(Token)的服务,默认的URL是/oauth/token.
    例如:AuthorizationEndpoint类:
    在这里插入图片描述

下面是配置一个资源服务必须要实现的过滤器:

  • OAuth2AuthenticationProcessingFilter:用来作为认证令牌(Token)的一个处理流程过滤器。只有当过滤器通过之后,请求者才能获得受保护的资源。

配置提供者(授权、资源)都可以通过简单的Java注解@Configuration来进行适配,你也可以使用基于XML的声明式语法来进行配置,如果你打算这样做的话,那么请使用http://www.springframework.org/schema/security/spring-security-oauth2.xsd来作为XML的schema(即XML概要定义)以及使用http://www.springframework.org/schema/security/oauth2来作为命名空间。

授权服务配置:

配置一个授权服务,你需要考虑几种授权类型(Grant Type),不同的授权类型为客户端(Client)提供了不同的获取令牌(Token)方式,为了实现并确定这几种授权,需要配置使用 ClientDetailsService 和 TokenService 来开启或者禁用这几种授权机制。到这里就请注意了,不管你使用什么样的授权类型(Grant Type),每一个客户端(Client)都能够通过明确的配置以及权限来实现不同的授权访问机制。这也就是说,假如你提供了一个支持"client_credentials"的授权方式,并不意味着客户端就需要使用这种方式来获得授权。下面是几种授权类型的列表,具体授权机制的含义可以参见RFC6749(中文版本):

  • authorization_code:授权码类型。
  • implicit:隐式授权类型。
  • password:资源所有者(即用户)密码类型。
  • client_credentials:客户端凭据(客户端ID以及Key)类型。
  • refresh_token:通过以上授权获得的刷新令牌来获取新的令牌。

可以用 @EnableAuthorizationServer 注解来配置OAuth2.0 授权服务机制,通过使用@Bean注解的几个方法一起来配置这个授权服务。下面咱们介绍几个配置类,这几个配置是由Spring创建的独立的配置对象,它们会被Spring传入AuthorizationServerConfigurer中:

  • ClientDetailsServiceConfigurer:用来配置客户端详情服务(ClientDetailsService),客户端详情信息在这里进行初始化,你能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息。
  • AuthorizationServerSecurityConfigurer:用来配置令牌端点(Token Endpoint)的安全约束.
  • AuthorizationServerEndpointsConfigurer:用来配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services)。
    (译者注:以上的配置可以选择继承AuthorizationServerConfigurerAdapter并且覆写其中的三个configure方法来进行配置。)

配置授权服务一个比较重要的方面就是提供一个授权码给一个OAuth客户端(通过 authorization_code 授权类型),一个授权码的获取是OAuth客户端跳转到一个授权页面,然后通过验证授权之后服务器重定向到OAuth客户端,并且在重定向连接中附带返回一个授权码。

如果你是通过XML来进行配置的话,那么可以使用 标签来进行配置。

(译者注:想想现在国内各大平台的社会化登陆服务,例如腾讯,用户要使用QQ登录到某个网站,这个网站是跳转到了腾讯的登陆授权页面,然后用户登录并且确定授权之后跳转回目标网站,这种授权方式规范在我上面提供的链接RFC6749的第4.1节有详细阐述。)

配置客户端详情信息(Client Details):

ClientDetailsServiceConfigurer (AuthorizationServerConfigurer 的一个回调配置项,见上的概述) 能够使用内存或者JDBC来实现客户端详情服务(ClientDetailsService),有几个重要的属性如下列表:

  • clientId:(必须的)用来标识客户的Id。
  • secret:(需要值得信任的客户端)客户端安全码,如果有的话。
  • scope:用来限制客户端的访问范围,如果为空(默认)的话,那么客户端拥有全部的访问范围。
  • authorizedGrantTypes:此客户端可以使用的授权类型,默认为空。
  • authorities:此客户端可以使用的权限(基于Spring Security authorities)。

客户端详情(Client Details)能够在应用程序运行的时候进行更新,可以通过访问底层的存储服务(例如将客户端详情存储在一个关系数据库的表中,就可以使用 JdbcClientDetailsService)或者通过 ClientDetailsManager 接口(同时你也可以实现 ClientDetailsService 接口)来进行管理。
(译者注:不过我并没有找到 ClientDetailsManager 这个接口文件,只找到了 ClientDetailsService)

管理令牌(Managing Token):

AuthorizationServerTokenServices 接口定义了一些操作使得你可以对令牌进行一些必要的管理,在使用这些操作的时候请注意以下几点:

  • 当一个令牌被创建了,你必须对其进行保存,这样当一个客户端使用这个令牌对资源服务进行请求的时候才能够引用这个令牌。
  • 当一个令牌是有效的时候,它可以被用来加载身份信息,里面包含了这个令牌的相关权限。

当你自己创建 AuthorizationServerTokenServices 这个接口的实现时,你可能需要考虑一下使用 DefaultTokenServices 这个类,里面包含了一些有用实现,你可以使用它来修改令牌的格式和令牌的存储。默认的,当它尝试创建一个令牌的时候,是使用随机值来进行填充的,除了持久化令牌是委托一个 TokenStore 接口来实现以外,这个类几乎帮你做了所有的事情。并且 TokenStore 这个接口有一个默认的实现,它就是 InMemoryTokenStore ,如其命名,所有的令牌是被保存在了内存中。除了使用这个类以外,你还可以使用一些其他的预定义实现,下面有几个版本,它们都实现了TokenStore接口:

  • InMemoryTokenStore:这个版本的实现是被默认采用的,它可以完美的工作在单服务器上(即访问并发量压力不大的情况下,并且它在失败的时候不会进行备份),大多数的项目都可以使用这个版本的实现来进行尝试,你可以在开发的时候使用它来进行管理,因为不会被保存到磁盘中,所以更易于调试。
  • JdbcTokenStore:这是一个基于JDBC的实现版本,令牌会被保存进关系型数据库。使用这个版本的实现时,你可以在不同的服务器之间共享令牌信息,使用这个版本的时候请注意把"spring-jdbc"这个依赖加入到你的classpath当中。
  • JwtTokenStore:这个版本的全称是 JSON Web Token(JWT),它可以把令牌相关的数据进行编码(因此对于后端服务来说,它不需要进行存储,这将是一个重大优势),但是它有一个缺点,那就是撤销一个已经授权令牌将会非常困难,所以它通常用来处理一个生命周期较短的令牌以及撤销刷新令牌(refresh_token)。另外一个缺点就是这个令牌占用的空间会比较大,如果你加入了比较多用户凭证信息。JwtTokenStore 不会保存任何数据,但是它在转换令牌值以及授权信息方面与 DefaultTokenServices 所扮演的角色是一样的。
JWT令牌(JWT Tokens):

使用JWT令牌你需要在授权服务中使用 JwtTokenStore,资源服务器也需要一个解码的Token令牌的类 JwtAccessTokenConverter,JwtTokenStore依赖这个类来进行编码以及解码,因此你的授权服务以及资源服务都需要使用这个转换类。Token令牌默认是有签名的,并且资源服务需要验证这个签名,因此呢,你需要使用一个对称的Key值,用来参与签名计算,这个Key值存在于授权服务以及资源服务之中。或者你可以使用非对称加密算法来对Token进行签名,Public Key公布在/oauth/token_key这个URL连接中,默认的访问安全规则是"denyAll()",即在默认的情况下它是关闭的,你可以注入一个标准的 SpEL 表达式到 AuthorizationServerSecurityConfigurer 这个配置中来将它开启(例如使用"permitAll()"来开启可能比较合适,因为它是一个公共密钥)。

如果你要使用 JwtTokenStore,请务必把"spring-security-jwt"这个依赖加入到你的classpath中。

配置授权类型(Grant Types):

授权是使用 AuthorizationEndpoint 这个端点来进行控制的,你能够使用 AuthorizationServerEndpointsConfigurer 这个对象的实例来进行配置(AuthorizationServerConfigurer 的一个回调配置项,见上的概述) ,如果你不进行设置的话,默认是除了资源所有者密码(password)授权类型以外,支持其余所有标准授权类型的(RFC6749),我们来看一下这个配置对象有哪些属性可以设置吧,如下列表:

  • authenticationManager:认证管理器,当你选择了资源所有者密码(password)授权类型的时候,请设置这个属性注入一个 AuthenticationManager 对象。
  • userDetailsService:如果啊,你设置了这个属性的话,那说明你有一个自己的 UserDetailsService 接口的实现,或者你可以把这个东西设置到全局域上面去(例如 GlobalAuthenticationManagerConfigurer 这个配置对象),当你设置了这个之后,那么 “refresh_token” 即刷新令牌授权类型模式的流程中就会包含一个检查,用来确保这个账号是否仍然有效,假如说你禁用了这个账户的话。
  • authorizationCodeServices:这个属性是用来设置授权码服务的(即 AuthorizationCodeServices 的实例对象),主要用于 “authorization_code” 授权码类型模式。
  • implicitGrantService:这个属性用于设置隐式授权模式,用来管理隐式授权模式的状态。
  • tokenGranter:这个属性就很牛B了,当你设置了这个东西(即 TokenGranter 接口实现),那么授权将会交由你来完全掌控,并且会忽略掉上面的这几个属性,这个属性一般是用作拓展用途的,即标准的四种授权模式已经满足不了你的需求的时候,才会考虑使用这个。

在XML配置中呢,你可以使用 “authorization-server” 这个标签元素来进行设置。

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

AuthorizationServerEndpointsConfigurer 这个配置对象(AuthorizationServerConfigurer 的一个回调配置项,见上的概述) 有一个叫做 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保护起来只供授权用户访问,我们来看看在标准的Spring Security中 WebSecurityConfigurer 是怎么用的。

复制代码

@Override
protected void configure(HttpSecurity http) throws Exception {
    http .authorizeRequests().antMatchers("/login").permitAll().and()
    // default protection for all resources (including /oauth/authorize)
    .authorizeRequests() .anyRequest().hasRole("USER")
    // ... more configuration, e.g. for form login
}

复制代码
注意:如果你的应用程序中既包含授权服务又包含资源服务的话,那么这里实际上是另一个的低优先级的过滤器来控制资源接口的,这些接口是被保护在了一个访问令牌(access token)中,所以请挑选一个URL链接来确保你的资源接口中有一个不需要被保护的链接用来取得授权,就如上面示例中的 /login 链接,你需要在 WebSecurityConfigurer 配置对象中进行设置。

令牌端点默认也是受保护的,不过这里使用的是基于 HTTP Basic Authentication 标准的验证方式来验证客户端的,这在XML配置中是无法进行设置的(所以它应该被明确的保护)。

在XML配置中可以使用 元素标签来改变默认的端点URLs,注意在配置 /check_token 这个链接端点的时候,使用 check-token-enabled 属性标记启用。

强制使用SSL(Enforcing SSL):

使用简单的HTTP请求来进行测试是可以的,但是如果你要部署到产品环境上的时候,你应该永远都使用SSL来保护授权服务器在与客户端进行通讯的时候进行加密。你可以把授权服务应用程序放到一个安全的运行容器中,或者你可以使用一个代理,如果你设置正确了的话它们应该工作的很好(这样的话你就不需要设置任何东西了)。
但是也许你可能希望使用 Spring Security 的 requiresChannel() 约束来保证安全,对于授权端点来说(还记得上面的列表吗,就是那个 /authorize 端点),它应该成为应用程序安全连接的一部分,而对于 /token 令牌端点来说的话,它应该有一个标记被配置在 AuthorizationServerEndpointsConfigurer 配置对象中,你可以使用 sslOnly() 方法来进行设置。当然了,这两个设置是可选的,不过在以上两种情况中,会导致Spring Security 会把不安全的请求通道重定向到一个安全通道中。(译者注:即将HTTP请求重定向到HTTPS请求上)。

自定义错误处理(Error Handling):

端点实际上就是一个特殊的Controller,它用于返回一些对象数据。
授权服务的错误信息是使用标准的Spring MVC来进行处理的,也就是 @ExceptionHandler 注解的端点方法,你也可以提供一个 WebResponseExceptionTranslator 对象。最好的方式是改变响应的内容而不是直接进行渲染。
假如说在呈现令牌端点的时候发生了异常,那么异常委托了 HttpMessageConverters 对象(它能够被添加到MVC配置中)来进行输出。假如说在呈现授权端点的时候未通过验证,则会被重定向到 /oauth/error 即错误信息端点中。whitelabel error (即Spring框架提供的一个默认错误页面)错误端点提供了HTML的响应,但是你大概可能需要实现一个自定义错误页面(例如只是简单的增加一个 @Controller 映射到请求路径上 @RequestMapping("/oauth/error"))。

映射用户角色到权限范围(Mapping User Roles to Scopes):

有时候限制令牌的权限范围是很有用的,这不仅仅是针对于客户端,你还可以根据用户的权限来进行限制。如果你使用 DefaultOAuth2RequestFactory 来配置 AuthorizationEndpoint 的话你可以设置一个flag即 checkUserScopes=true来限制权限范围,不过这只能匹配到用户的角色。你也可以注入一个 OAuth2RequestFactory 到 TokenEnpoint 中,不过这只能工作在 password 授权模式下。如果你安装一个 TokenEndpointAuthenticationFilter 的话,你只需要增加一个过滤器到 HTTP BasicAuthenticationFilter 后面即可。当然了,你也可以实现你自己的权限规则到 scopes 范围的映射和安装一个你自己版本的 OAuth2RequestFactory。AuthorizationServerEndpointConfigurer 配置对象允许你注入一个你自定义的 OAuth2RequestFactory,因此你可以使用这个特性来设置这个工厂对象,前提是你使用 @EnableAuthorizationServer 注解来进行配置(见上面介绍的授权服务配置)。

授权服务器配置实战

授权服务器上必须有登录的用户名和密码信息(只有已经登录的用户才能使用授权),并且配置允许授权的客户端信息
具体过程参考 :

配置springsecurity授权

添加maven依赖

 <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.8.RELEASE</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.security.oauth.boot</groupId>
            <artifactId>spring-security-oauth2-autoconfigure</artifactId>
            <version>2.1.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <version>1.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>nekohtml</groupId>
            <artifactId>nekohtml</artifactId>
            <version>1.9.6.2</version>
        </dependency>
    </dependencies>

添加springsecurity的配置类

package lzeqian;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

/**
 * @author 廖敏
 * 创建日期 2019-03-12 16:15
 **/
@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    public UserDetailsService userDetailsService11() {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("test").password(new BCryptPasswordEncoder().encode("123456")).roles("USER").build());
        return manager;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                    .antMatchers("/test").authenticated()
                    .regexMatchers("/images/.+\\.jpg").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                    .loginPage("/rlogin")
                    .loginProcessingUrl("/myauth")
                    .failureForwardUrl("/rlogin")
                    .usernameParameter("user")
                    .passwordParameter("pass")

                    .permitAll()
                //注意 这里用false因为登录成功后,调回之前输入的授权页面
                .defaultSuccessUrl("/toSuc",false)

                .and()
                .httpBasic().and().csrf().disable();

    }
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(userDetailsService11()).passwordEncoder(new BCryptPasswordEncoder());
    }
}

添加控制层的登录和成功页面

@Controller
public class TestController {
    @RequestMapping("/toSuc")
    public String suc() {
        return "suc";
    }
    @RequestMapping("/rlogin")
    public String rlogin(String name) {
        return "lg";
    }
}

添加登录模板/src/main/resource/templates/lg.html

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <meta charset="utf-8">
    <style type="text/css">
        fieldset{width: 350px;background:#4876FF;height: 200px;margin: 150px 400px 0px}
        button{maogin: 0; padding: 0}


    </style>
</head>
<body bgcolor="#7EC0EE">
<form action="/myauth" method="post">
        <label>用户名:</label>
        <input type="text"  name="user" value="test">(请输入用户名)</br></br>
        <label>密  码:</label>
        <input type="password" name="pass"  value="123456">(输入密码)<br><br>
        <input type="submit" value="提交">
</form>
</body>
</html>

添加成功页面模板suc.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
 this is success page
</body>
</html>

application.properties配置

server.port=8889
spring.thymeleaf.cache=false
spring.thymeleaf.mode=LEGACYHTML5

测试登录是否跳转到登录页,输入用户名test和密码123456是否能成功跳转到成功页面

配置认证服务器

添加认证服务器配置类

@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    /**
    配置客户端详情服务
    **/
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {

        //添加客户端信息
        clients.inMemory()                  // 使用in-memory存储客户端信息
                .withClient("client")       // 获取授权码必须指定的客户端标识
                .secret("{noop}secret")                   // client_secret 需要加上{noop}指定使用NoOpPasswordEncoder给DelegatingPasswordEncoder去校验密码
                .authorizedGrantTypes("authorization_code")     // 该client允许的授权类型
                .redirectUris("http://www.baidu.com")
                .scopes("all");                     // 允许的授权范围

    }
 /**
    用来配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services)。
    **/
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        super.configure(endpoints);
        endpoints.tokenStore(new JwtTokenStore(new JwtAccessTokenConverter()));
    }
 /**
    用来配置令牌端点(Token Endpoint)的安全约束.
    **/
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        super.configure(security);
        //security.passwordEncoder(NoOpPasswordEncoder.getInstance());
    }
}

模拟测试认证服务器

接下来模拟客户端获取授权码

localhost:8889/oauth/authorize?client_id=client&response_type=code&redirect_uri=http://www.baidu.com

因为第一次访问,直接跳转到登陆页面
在这里插入图片描述
输入用户名和密码后,跳转到用户授权页面
在这里插入图片描述
点击 authrize 授权 跳转到www.baidu.com带上了code授权码
在这里插入图片描述
接下来模拟通过授权码获取token,如果linux可以通过

curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 
'grant_type=authorization_code&code=uHJ1Lt&redirect_uri=http://www.baidu.com' 
"http://client:secret@localhost:8889/oauth/token"

也可以通过idea的rest测试工具
在这里插入图片描述
填写参数点击左上角绿色测试箭头
在这里插入图片描述
在response也签中返回了token

{"access_token":"cd51e083-95b2-4c71-9774-5a07de116345","token_type":"bearer","expires_in":43199,"scope":"all"}

如果希望在token上带上额外信息
修改oauth2配置类

@Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        super.configure(endpoints);
        endpoints.tokenStore(new JwtTokenStore(new JwtAccessTokenConverter()))
                //添加额外信息到token中
                .tokenEnhancer(new TokenEnhancer() {
                    @Override
                    public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {
                        DefaultOAuth2AccessToken doat= (DefaultOAuth2AccessToken) oAuth2AccessToken;
                        Map<String, Object> additionalInfo = new HashMap<>();
                        additionalInfo.put("myname", "jiaozi");
                        doat.setAdditionalInformation(additionalInfo);
                        return doat;
                    }
                })
        ;
    }

再次测试返回

{"access_token":"a675a848-227d-4951-937c-5d2b36282c26","token_type":"bearer","expires_in":43199,"scope":"all","myname":"jiaozi"}
使用jdbc存储

jdbc对应表:https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/test/resources/schema.sql
配置data-jpa,配置数据源(不给出)

public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.withClientDetails(new JdbcClientDetailsService(配置DataSource类));
}
替换授权页

默认授权页
在这里插入图片描述
通过文章 https://projects.spring.io/spring-security-oauth/docs/oauth2.html 了解

The URL paths provided by the framework are /oauth/authorize (the authorization endpoint), /oauth/token (the token endpoint), /oauth/confirm_access (user posts approval for grants here), /oauth/error (used to render errors in the authorization server), /oauth/check_token (used by Resource Servers to decode access tokens), and /oauth/token_key (exposes public key for token verification if using JWT tokens).

实际授权页的路径是:/oauth/confirm_access
可以替换成自己的授权页
修改oauth2入口

@Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        super.configure(endpoints);
        endpoints.pathMapping("/oauth/confirm_access ","/extenal/oauth/confirm_access")///extenal表示外部的
}

定义一个控制层类 路径是:/oauth/confirm_access

@Controller
public class OAuth2ApprovalController {

    @RequestMapping("/oauth/confirm_access")
    public String getAccessConfirmation(Map<String, Object> model, HttpServletRequest request)
            throws Exception {
        return "approval";
    }

}

新建approval的html页面

<html>
<body><h1>OAuth 确认</h1>
<p>您是否希望 "client" 访问受保护的资源?</p>
<form id="confirmationForm" name="confirmationForm" action="/oauth/authorize" method="post"><input
        name="user_oauth_approval" value="true" type="hidden"/><label>
    <input name="authorize" value="授权"
                                                                             type="submit"/></label></form>
<form id="denialForm" name="denialForm" action="/oauth/authorize" method="post">
    <input name="user_oauth_approval"
           value="false" type="hidden"/><label>
    <input
        name="deny" value="禁止" type="submit"/></label></form>
</body>
</html>

最后授权页面
在这里插入图片描述
如果不配置入口也可以直接定义接口但是要使用SessionAttributes注解

@Controller  
@SessionAttributes("authorizationRequest")  
public class OAuth2ApprovalController {  
  
   @RequestMapping("/oauth/confirm_access")
    public String getAccessConfirmation(Map<String, Object> model, HttpServletRequest request)
            throws Exception {
        return "approval";
    }
  
}  
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值