OAuth2
文章目录
一、OAuth2是什么?
1、快递员问题:
我经常网购和外卖,每天都有快递员来送货。我必须找到一个办法,让快递员通过门禁系统,进入小区。
- 远程开门
- 告诉密码
- 授权范围,只能访问小区和单元门
业主帮忙每次开门,未来几天都能访问
如果我把自己的密码,告诉快递员,他就拥有了与我同样的权限,这样好像不太合适。万一我想取消他进入小区的权力,也很麻烦,我自己的密码也得跟着改了,还得通知其他的快递员。
有没有一种办法,让快递员能够自由进入小区,又不必知道小区居民的密码,而且他的唯一权限就是送货,其他需要密码的场合,他都没有权限?
快递员授权机制设计
第一步,门禁系统的密码输入器下面,增加一个按钮,叫做"获取授权"。快递员需要首先按这个按钮,去申请授权。
第二步,他按下按钮以后,屋主(也就是我)的手机就会跳出对话框:有人正在要求授权。系统还会显示该快递员的姓名、工号和所属的快递公司。我确认请求属实,就点击按钮,告诉门禁系统,我同意给予他进入小区的授权。
第三步,门禁系统得到我的确认以后,向快递员显示一个进入小区的令牌(access token)。令牌就是类似密码的一串数字,只在短期内(比如七天)有效。
第四步,快递员向门禁系统输入令牌,进入小区。
有人可能会问,为什么不是远程为快递员开门,而要为他单独生成一个令牌?这是因为快递员可能每天都会来送货,第二天他还可以复用这个令牌。另外,有的小区有多重门禁,快递员可以使用同一个令牌通过它们。
2、令牌和密码
令牌(token)与密码(password)的作用是一样的,都可以进入系统,但是有三点差异。
1) 令牌是短期的,到期会自动失效,用户自己无法修改。密码一般长期有效,用户不修改,就不会发生变化
2) 令牌可以被数据所有者撤销,会立即失效。以上例而言,屋主可以随时取消快递员的令牌。密码一般不允许被他人撤销。
3) 令牌有权限范围(scope),比如只能进小区的二号门。对于网络服务来说,只读令牌就比读写令牌更安全。密码一般是完整权限。
上面这些设计,保证了令牌既可以让第三方应用获得权限,同时又随时可控,不会危及系统安全。这就是 OAuth 2.0 的优点。
注意,只要知道了令牌,就能进入系统。系统一般不会再次确认身份,所以令牌必须保密,泄漏令牌与泄漏密码的后果是一样的。 这也是为什么令牌的有效期,一般都设置得很短的原因。
3、京东和QQ的故事
OAuth简单说就是一种授权的协议,只要授权方和被授权方遵守这个协议去写代码提供服务,那双方就是实现了OAuth模式。
举个例子,你想登录京东,但你又从来没注册过京东账号,又不想新注册新账号访问京东,怎么办呢?不用担心,京东已经为你这种懒人做了准备,用你的qq号可以授权给京东进行登录。
3.1 在京东官网点击QQ登录
3.2 跳转到QQ页面输入用户名和密码,然后点击授权登录
3.3 跳转到京东页面,成功登录
上述例子中的京东就是客户端,QQ就是认证服务器,OAuth2.0就是客户端和认证服务器之间由于相互不信任而产生的一个授权协议,要是相互信任那QQ直接把自己数据库给京东好了,你直接在京东输入qq账号密码查下数据库验证就可以登录,还跳来跳去的多麻烦。
4、OAuth2是什么?
OAuth2是目前最流行的授权协议,用来授权第三方应用,获取用户数据
4.1、OAuth2角色有哪些?
资源所有者(Resource Owner):授权客户端访问本身资源信息的用户,例子中拥有QQ的用户
客户端(Client):即代表意图访问受限资源的第三方应用,例子中的京东
授权服务器(Authorization Server):授权服务器用来验证用户提供的信息是否正确,例子中的QQ服务器
资源服务器(Resource Server):资源服务器是提供给用户资源的服务器,例子中的QQ服务器
4.2、OAuth2授权模式
Oauth2授权模式分为授权码模式、简化模式、密码模式和客户端模式,分别解析如下。
- 授权码模式:授权码模式(authorization code)是功能最完整、流程最严谨的授权模式。它的特点就是通过客户端的服务器与授权服务器进行交互,国内常见的第三方平台登录功能基本 都是使用这种模式。
- 简化模式:简化模式不需要客户端服务器参与,直接在浏览器中向授权服务器中请令牌,一般若网站是纯静态页面,则可以采用这种方式。
- 密码模式:密码模式是用户把用户名密码直接告诉客户端,客户端使用这些信息向授权服务器中请令牌。这需要用户对客户端高度信任,例如客户端应用和服务提供商是同一家公司。
- 客户端模式:客户端模式是指客户端使用自己的名义而不是用户的名义向服务提供者申请授权。严格来说,客户端模式并不能算作 OAuth 协议要解决的问题的一种解决方案,但是,对于开发者而言,在一些前后端分离应用或者为移动端提供的认证授权服务器上使用这种模式还是非常方便的。
二、为什么使用OAuth2
1、单体架构登录
2、微服务架构方案1
Session共享
3、微服务架构方案2
基于Token
4、cookie session和token的区别
- cookie是不能跨域的,前后端分离分布式架构实现多系统SSO非常困难
- 移动端应用没有cookie,所以对于移动端支持不好
- token基于header传递,部分解决了CSRF攻击
- token要比sessionID大,客户端存储在Local Storage中,可以直接被JS读取
参考文章:Session、Cookie、Token 【浅谈三者之间的那点事】 - 云+社区 - 腾讯云 (tencent.com)
Cookie和Session
HTTP 协议是一种无状态协议
,即每次服务端接收到客户端的请求时,都是一个全新的请求,服务器并不知道客户端的历史请求记录;Session 和 Cookie 的主要目的就是为了弥补 HTTP 的无状态特性。
Session是什么
客户端请求服务端,服务端会为这次请求开辟一块内存空间
,这个对象便是 Session 对象,存储结构为 ConcurrentHashMap
。Session 弥补了 HTTP 无状态特性,服务器可以利用 Session 存储客户端在同一个会话期间的一些操作记录。
Session如何判断是否是同一会话?
服务器第一次接收到请求时,开辟了一块 Session 空间(创建了Session对象),同时生成一个 sessionId ,并通过响应头的 **Set-Cookie:JSESSIONID=XXXXXXX **命令,向客户端发送要求设置 Cookie 的响应; 客户端收到响应后,在本机客户端设置了一个 **JSESSIONID=XXXXXXX **的 Cookie 信息,该 Cookie 的过期时间为浏览器会话结束;
接下来客户端每次向同一个网站发送请求时,请求头都会带上该 Cookie信息(包含 sessionId ), 然后,服务器通过读取请求头中的 Cookie 信息,获取名称为 JSESSIONID 的值,得到此次请求的 sessionId。
Session的缺点
Session 机制有个缺点,比如 A 服务器存储了 Session,就是做了负载均衡后,假如一段时间内 A 的访问量激增,会转发到 B 进行访问,但是 B 服务器并没有存储 A 的 Session,会导致 Session 的失效。
Cookie是什么
HTTP 协议中的 Cookie 包括 Web Cookie
和浏览器 Cookie
,它是服务器发送到 Web 浏览器的一小块数据。服务器发送到浏览器的 Cookie,浏览器会进行存储,并与下一个请求一起发送到服务器。通常,它用于判断两个请求是否来自于同一个浏览器,例如用户保持登录状态。
Cookie的应用:
- 会话管理
- 登录、购物车、游戏得分或者服务器应该记住的其他内容
- 个性化
- 用户偏好、主题或者其他设置
- 追踪
- 记录和分析用户行为
Json Web Token和Session Cookies的对比
JSON Web Token ,简称 JWT
,它和 Session
都可以为网站提供用户的身份认证,但是它们不是一回事。
它们既可以对用户进行身份验证,也可以用来在用户单击进入不同页面时以及登陆网站或应用程序后进行身份验证。
如果没有这两者,那你可能需要在每个页面切换时都需要进行登录了。因为 HTTP 是一个无状态的协议。这也就意味着当你访问某个网页,然后单击同一站点上的另一个页面时,服务器的内存中
将不会记住你之前的操作。
因此,如果你登录并访问了你有权访问的另一个页面,由于 HTTP 不会记录你刚刚登录的信息,因此你将再次登录。
JWT 和 Session Cookies 就是用来处理在不同页面之间切换,保存用户登录信息的机制。
也就是说,这两种技术都是用来保存你的登录状态,能够让你在浏览任意受密码保护的网站。通过在每次产生新的请求时对用户数据进行身份验证来解决此问题。
所以 JWT 和 Session Cookies 的相同之处是什么?那就是它们能够支持你在发送不同请求之间,记录并验证你的登录状态的一种机制。
什么是Session Cookies
Session Cookies 也称为会话 Cookies
,在 Session Cookies 中,用户的登录状态会保存在服务器
的内存
中。当用户登录时,Session 就被服务端安全的创建。
在每次请求时,服务器都会从会话 Cookie 中读取 SessionId,如果服务端的数据和读取的 SessionId 相同,那么服务器就会发送响应给浏览器,允许用户登录。
token 令牌,是用户身份的验证方式。 最简单的token组成:uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名)。
三、项目实战
1、创建授权服务器(auth-server)
1.1 引入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.lxs</groupId>
<artifactId>auth-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>auth-server</name>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR5</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-oauth2-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<!--security-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
1.2 配置文件
server:
port: 8888
1.3 在启动器中开启资源服务器
package com.lxs.oauth2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
@SpringBootApplication
//开启资源服务器
@EnableResourceServer
public class AuthServerApplication {
public static void main(String[] args) {
SpringApplication.run(AuthServerApplication.class, args);
}
}
1.4 自定义OAuth2配置文件
需要继承AuthorizationServerConfigurerAdapter,重载3个configure方法
package com.lxs.oauth2.config;
import org.bouncycastle.util.encoders.Base64Encoder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import sun.misc.BASE64Decoder;
@Configuration
//开启授权服务
@EnableAuthorizationServer
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
private static final String CLIENT_ID = "cms";
private static final String SECRET_CHAR_SEQUENCE = "{noop}secret";
private static final String SCOPE_READ = "read";
private static final String SCOPE_WRITE = "write";
private static final String TRUST = "trust";
private static final String USER ="user";
private static final String ALL = "all";
private static final int ACCESS_TOKEN_VALIDITY_SECONDS = 30*60;
private static final int FREFRESH_TOKEN_VALIDITY_SECONDS = 30*60;
// 密码模式授权模式
private static final String GRANT_TYPE_PASSWORD = "password";
//授权码模式
private static final String AUTHORIZATION_CODE = "authorization_code";
//refresh token模式
private static final String REFRESH_TOKEN = "refresh_token";
//简化授权模式
private static final String IMPLICIT = "implicit";
//客户端模式
private static final String CLIENT_CREDENTIALS="client_credentials";
//指定哪些资源是需要授权验证的
private static final String RESOURCE_ID = "resource_id";
/**
* 配置客户端信息
* @param clients
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
//使用内存存储
.inMemory()
//标记客户端id
.withClient(CLIENT_ID)
//客户端安全码/密钥
.secret(SECRET_CHAR_SEQUENCE)
//设置为true,直接自动授权,成功返回授权码
.autoApprove(true)
//授权后重定向的地址
.redirectUris("http://127.0.0.1:8084/cms/login")
//允许授权的范围
.scopes(ALL)
//访问令牌的时效
.accessTokenValiditySeconds(ACCESS_TOKEN_VALIDITY_SECONDS)
//刷新令牌的时效
.refreshTokenValiditySeconds(FREFRESH_TOKEN_VALIDITY_SECONDS)
//允许授权的类型
.authorizedGrantTypes(AUTHORIZATION_CODE);
}
/**
* oauth2端点的权限配置
* @param security
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
//配置/oauth/token_key可以匿名访问
.tokenKeyAccess("permitAll()")
// .tokenKeyAccess("isAuthenticated()")//设置认证后访问
//开启令牌验证端点/oauth/check_token端点匿名访问
.checkTokenAccess("permitAll()")
//允许表单认证
.allowFormAuthenticationForClients();
super.configure(security);
}
/**
* token存储方式
* @param endpoints
* @throws Exception
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager).tokenStore(memoryTokenStore());
}
@Bean
public TokenStore memoryTokenStore() {
// 最基本的InMemoryTokenStore生成token
return new InMemoryTokenStore();
}
}
1.5 配置Spring security
package com.lxs.oauth2.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableWebSecurity
@Order(1) //值越小,优先级越高
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception { //auth.inMemoryAuthentication()
auth.inMemoryAuthentication()
.withUser("lxs")
.password("{noop}123")
.roles("admin");
}
@Override
public void configure(WebSecurity web) throws Exception {
//解决静态资源被拦截的问题
web.ignoring().antMatchers("/asserts/**");
web.ignoring().antMatchers("/favicon.ico");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http // 配置登录页并允许访问
.formLogin().permitAll()
// 配置Basic登录
//.and().httpBasic()
// 配置登出页面
.and().logout().logoutUrl("/logout").logoutSuccessUrl("/")
// 配置允许访问的链接
.and().authorizeRequests().antMatchers("/oauth/**", "/login/**", "/logout/**", "/api/**").permitAll()
// 其余所有请求全部需要鉴权认证
.anyRequest().authenticated()
// 关闭跨域保护;
.and().csrf().disable();
}
}
1.6 验证
启动auth-server项目,在浏览器中访问 http://localhost:8888/oauth/check_token?token=123456789
会出现如下信息,说明验证令牌端点可以访问
2、创建资源服务器(cms)
2.1 引入依赖
和授权服务器依赖一致
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.lxs</groupId>
<artifactId>auth-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>auth-server</name>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR5</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Hoxton.SR5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.2 配置文件
server:
port: 8084
servlet:
context-path: /cms
2.3 在启动器中开启资源服务器
package com.lxs.oauth2;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
@SpringBootApplication
//开启资源服务器
@EnableResourceServer
public class CmsApplication {
public static void main(String[] args) {
SpringApplication.run(CmsApplication.class, args);
}
}
2.4 创建一个Controller用于测试
package com.lxs.oauth2.controller;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/getCurrentUser")
public Object getCurrentUser(Authentication authentication) {
return authentication;
}
@GetMapping("/index")
public String index() {
return "index";
}
}
2.5 自定义OAuth2资源服务器配置
继承ResourceServerConfigurerAdapter,重写configure方法
package com.lxs.oauth2.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.RemoteTokenServices;
@Configuration
public class Oauth2ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
//令牌校验端点
private static final String CHECK_TOKEN_URL = "http://localhost:8888/oauth/check_token";
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
//使用远程TokenService验证令牌,这种验证方式,每次验证都提交给授权服务器,效率较低
//验证地址http://localhost:8888/oauth/check_token
RemoteTokenServices tokenServices = new RemoteTokenServices();
tokenServices.setCheckTokenEndpointUrl(CHECK_TOKEN_URL);
//客户端id和秘钥在服务器端产生
tokenServices.setClientId("cms");
tokenServices.setClientSecret("secret");
resources.tokenServices(tokenServices);
}
}
2.6 配置Spring security
继承WebSecurityConfigurerAdapter
package com.lxs.oauth2.config;
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;
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/**").authenticated();
//禁用CSRF
http.csrf().disable();
}
}
2.7 验证
启动cms项目,在浏览器中访问 http://localhost:8084/cms/index
因为没有授权服务器颁发令牌,所以没有权限
3、OAuth2授权模式回顾
OAuth2有如下四种授权模式
- 授权码模式
- 简化模式
- 密码模式
- 客户端模式
4、授权码模式
4.1 授权码模式流程
- 客户端携带client_id、redirect_uri,中间通过代理者访问授权服务器,如果已经登录过会直接返回redirect_uri,没有登录过就跳转到登录页面
- 授权服务器对客户端进行身份验证(通过用户代理、让用户输入用户名和密码)
- 授权通过,会重定向到redirect_uri并携带授权码code作为uri参数
- 客户端携带授权码访问授权服务器
- 授权服务器验证授权码通过,返回accessToken(令牌)
授权码大概分为两部分,先获取授权码,再用该授权码获取令牌。
4.2 授权模式配置
在auth-server项目中的OAuth2Config.java中配置authorizedGrantTypes为authorization_code
4.3 测试
4.3.1 启动auth-server和cms
4.3.2 申请授权码
访问授权链接,在浏览器访问就可以,授权码模式response_type参数传code;
Get请求
http://localhost:8888/oauth/authorize?client_id=cms&client_secret=secret&response_type=code
参数列表如下:
client_id:客户端id,和授权配置类中设置的客户端id一致
response_type:授权码模式固定为code
scop:客户端范围,和授权配置类中的scop一致
redirect_uri:跳转uri,当授权码申请成功后会跳转到此地址,并在后面带上code参数(授权码)
因为没登录,所以会返回SpringSecurity的默认登录页面
输入在SpringSecurity中设置的账号和密码登录
登录成功,返回redirect_uri,拿到授权码
如果登录失败或者报错,清除一下浏览器缓存,再重新登录
4.3.3 通过授权码获得令牌
使用postman进行测试
输入地址
Post请求
http://localhost:8888/oauth/token?code=2WZBGO&grant_type=authorization_code&redirect_uri=http://127.0.0.1:8084/cms/login&scope=all
刚刚测试了一下,redirect_uri要和授权服务器中的oauth配置中的redirectUris保持一致,不然会报错,
用户名和密码是授权服务器中授权配置的客户端id和客户端密钥
如果再用相同的授权码或错误的授权码,都不能获得令牌
返回信息如下
access_token:访问令牌,携带此令牌访问资源
token_type:有MAC Token与Bearer Token两种类型,两种的校验算法不同,RFC 6750建议OAuth2采用Bearer
refresh_token:刷新令牌,使用此令牌可以延长访问令牌的过期时间
expores_in:过期时间,单位为秒
scope:范围,与定义的客户端范围一直
jti:当前token的唯一标识
refresh_token需要在授权服务器的oauth2配置中添加
重启服务,再次按照上面的步骤测试,得到结果
4.3.4 校验令牌
访问地址:http://localhost:8888/oauth/check_token?token=58e173f2-3efc-416c-97bc-08a8d60283ad
注意:token后面携带的参数是刚从postman中获取的令牌
返回如下信息
4.3.5 使用令牌(访问资源服务器)
不使用令牌访问/index服务
使用令牌访问,在路径后面添加access_token=刚刚获取的令牌
这样,就可以通过令牌访问受限资源了
5、简化模式(隐藏式)
5.1 简化模式流程
简化模式(implicit grant type),也称为隐藏式,不通过第三方应用程序的服务器,直接在浏览器中向认证服务器申请令牌,跳过了"授权码"这个步骤,因此称简化模式。简化模式是相对于授权码模式而言的
简化模式,流程如下:
- 客户端携带client_id,redirect_uri,中间通过代理者访问授权服务器,如果已经登录会直接返回redirect_uri,没有登录就跳转到登录页面
- 授权服务器对客户端进行身份验证(通过用户代理,让用户输入用户名和密码)
- 授权通过,会重定向到redirect_uri并携带授权码Access token作为uri参数
- 验证token通过,返回资源
对比授权码模式
区别在于,授权码模式,浏览器不持有令牌,浏览器只持有授权码(只能使用一次),Client通过授权码去授权服务器换一次令牌,相对安全。简化模式是授权服务器直接把令牌返回给浏览器,这样没有授权码模式安全了。
5.2 简化模式配置
在auth-server项目中的OAuth2Config.java中配置authorizedGrantTypes为implicit
5.3 测试
5.3.1 启动auth-server和cms
5.3.2 申请令牌
访问授权链接,在浏览器访问就可以,简化模式response_type参数传token!!授权码模式传的是code!!
Get请求
http://localhost:8888/oauth/authorize?client_id=cms&redirect_uri=http://127.0.0.1:8084/cms/login&response_type=token&scope=all
参数列表如下:
client_id:客户端id,和授权配置类中设置的客户端id保持一致
response_type:简化模式固定为token
scope:客户端范围,和授权配置类中设置的scope一致
redirect_uri:跳转uri
如果没登录,会跳转到SpringSecurity默认的登录页面,登录后,会看到如下信息
可以看见,直接把令牌通过参数返回了
5.3.3 校验令牌
访问地址:http://localhost:8888/oauth/check_token?token=efc6c513-e7f5-4b13-a398-9ffd269f3384
注意:token后面携带的参数是刚从浏览器中获取的access_token令牌
返回信息如下
5.3.4 使用令牌(访问资源服务器)
如果没有令牌,访问/index服务
使用令牌访问:http://localhost:8084/cms/index?access_token=efc6c513-e7f5-4b13-a398-9ffd269f3384
access_token后面是刚刚获取的令牌
这样就可以成功访问受限资源了
6、密码模式
6.1 密码模式流程
密码模式(resource owner password credentials):密码模式中,用户向客户端提供自己的用户名和密码,这通常用在用户对客户端高度信任的情况
密码模式流程如下:
- 用户访问客户端,提供uri连接包含用户名和密码信息给授权服务器
- 授权服务器对客户端进行身份验证
- 授权通过,返回access token给客户端
对比授权码模式和简化模式
6.2 密码模式配置
在auth-server项目中的OAuth2Config.java中配置authorizedGrantTypes为password
6.3 测试
6.3.1 启动auth-server和cms
6.3.2 申请令牌
此链接需要使用 http Basic认证。 什么是http Basic认证? http协议定义的一种认证方式,将客户端id和客户端密码按照“客户端ID:客户端密码”的格式拼接,并用base64编码,放在header中请求服务端,一个例子: Authorization:Basic WGNXZWJBcHA6WGNXZWJBcHA=WGNXZWJBcHA6WGNXZWJBcHA= 是用户名:密码的base64编码。 认证失败服务端返回 401 Unauthorized。
使用Postman进行测试:
得到结果:
6.3.3 校验令牌
访问地址:http://localhost:8888/oauth/check_token?token=efc6c513-e7f5-4b13-a398-9ffd269f3384
注意:token后面携带的参数是刚从postman中获取的access_token令牌
6.3.4 使用令牌(访问资源服务器)
使用令牌访问:http://localhost:8084/cms/index?access_token=efc6c513-e7f5-4b13-a398-9ffd269f3384
access_token后面是刚刚获取的令牌
7、客户端模式
7.1 客户端模式流程
客户端模式(client credentials):客户端模式(client credentials)适用于没有前端的命令行应用,即在命令行下请求令牌
客户端模式,流程如下:
-
第一步:获取token
-
第二步,拿到accessToken后,就可以直接访问资源
7.2 客户端模式配置
在auth-server项目中的OAuth2Config.java中配置authorizedGrantTypes为client_credentials
7.3 测试
7.3.1 启动auth-server和cms
7.3.2 申请令牌
使用postman进行测试
Post请求
访问 http://localhost:8888/oauth/token?client_id=cms&client_secret=secret&grant_type=client_credentials&scope=all
7.3.3 校验令牌
访问地址:http://localhost:8888/oauth/check_token?token=efc6c513-e7f5-4b13-a398-9ffd269f3384
注意:token后面携带的参数是刚从postman中获取的access_token令牌
7.3.4 使用令牌(访问资源服务器)
使用令牌访问:http://localhost:8084/cms/index?access_token=efc6c513-e7f5-4b13-a398-9ffd269f3384
access_token后面是刚刚获取的令牌
客户端模式和密码模式一样,同样适用于前端分离的微服务架构中实现SSO