一.参考文章

  1. Spring Security OAuth2 开发指南
  2. 理解OAuth2.0
    3.Spring Security oAuth2简介

二.oauth2知识整理

2.1 基本概念

oauth:

OAuth就是一种授权机制。数据的所有者告诉系统,同意授权第三方应用进入系统,获取这些数据。系统从而产生一个短期的进入令牌(token),用来代替密码,供第三方应用使用。

JWT:

JSON Web Token,是JSON风格的轻量级授权和认证规范,可以实现无状态,分布式的web应用授权。JWT的内容由三部分组成,分别是Header,Payload,Signature,三个部分之间通过.连接。

Access Token:

Access Token 是客户端访问资源服务器的令牌。拥有这个令牌代表着得到用户的授权。然而,这个授权应该是 临时 的,有一定有效期。Access Token 在使用的过程中 可能会泄露。给 Access Token 限定一个 较短的有效期 可以降低因 Access Token 泄露而带来的风险。然而引入了有效期之后,客户端使用起来就不那么方便了。每当 Access Token 过期,客户端就必须重新向用户索要授权。这样用户可能每隔几天,甚至每天都需要进行授权操作。这是一件非常影响用户体验的事情。希望有一种方法,可以避免这种情况。于是 oAuth2.0 引入了 Refresh Token 机制

Refresh Token:

Refresh Token 的作用是用来刷新 Access Token。认证服务器提供一个刷新接口。
传入 refresh_token 和 client_id,认证服务器验证通过后,返回一个新的 Access Token。为了安全,oAuth2.0 引入了两个措施:

1.Refresh Token 一定是保存在客户端的服务器上 ,而绝不能存放在狭义的客户端(例如 App、PC 端软件)上。调用 refresh 接口的时候,一定是从服务器到服务器的访问。

2.client_secret 机制。即每一个 client_id 都对应一个 client_secret。这个 client_secret 会在客户端申请 client_id 时,随 client_id 一起分配给客户端。客户端必须把 client_secret 妥善保管在服务器上,决不能泄露。刷新 Access Token 时,需要验证这个 client_secret。

2.2 OAuth的思路

OAuth在"客户端"与"服务提供商"之间,设置了一个授权层(authorization layer)。"客户端"不能直接登录"服务提供商",只能登录授权层,以此将用户与客户端区分开来。"客户端"登录授权层所用的令牌(token),与用户的密码不同。用户可以在登录的时候,指定授权层令牌的权限范围和有效期。
"客户端"登录授权层以后,"服务提供商"根据令牌的权限范围和有效期,向"客户端"开放用户储存的资料。

2.3 OAuth 2.0的四种授权方式

OAuth 2.0 规定了四种获得令牌的流程。不管哪一种授权方式,第三方应用申请令牌之前,都必须先到系统备案,说明自己的身份,然后会拿到两个身份识别码:客户端 ID(client ID)和客户端密钥(client secret)。这是为了防止令牌被滥用,没有备案过的第三方应用,是不会拿到令牌的。

2.3.1授权码模式(authorization code)

授权码(authorization code)方式,指的是第三方应用先申请一个授权码,然后再用该码获取令牌。

(A)用户访问客户端,后者将前者导向认证服务器。

(B)用户选择是否给予客户端授权。

(C)假设用户给予授权,认证服务器将用户导向客户端事先指定的"重定向URI"(redirection URI),同时附上一个授权码。

(D)客户端收到授权码,附上早先的"重定向URI",向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。

(E)认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。

2.3.2简化模式(implicit)

简化模式(implicit grant type)不通过第三方应用程序的服务器,直接在浏览器中向认证服务器申请令牌,跳过了"授权码"这个步骤,因此得名。所有步骤在浏览器中完成,令牌对访问者是可见的,且客户端不需要认证。

(A)客户端将用户导向认证服务器。

(B)用户决定是否给于客户端授权。

(C)假设用户给予授权,认证服务器将用户导向客户端指定的"重定向URI",并在URI的Hash部分包含了访问令牌。

(D)浏览器向资源服务器发出请求,其中不包括上一步收到的Hash值。

(E)资源服务器返回一个网页,其中包含的代码可以获取Hash值中的令牌。

(F)浏览器执行上一步获得的脚本,提取出令牌。

(G)浏览器将令牌发给客户端。

2.3.3密码模式(resource owner password credentials)

密码模式(Resource Owner Password Credentials Grant)中,用户向客户端提供自己的用户名和密码。客户端使用这些信息,向"服务商提供商"索要授权。

(A)用户向客户端提供用户名和密码。

(B)客户端将用户名和密码发给认证服务器,向后者请求令牌。

(C)认证服务器确认无误后,向客户端提供访问令牌。

2.3.4客户端模式(client credentials)

客户端模式(Client Credentials Grant)指客户端以自己的名义,而不是以用户的名义,向"服务提供商"进行认证。严格地说,客户端模式并不属于OAuth框架所要解决的问题。在这种模式中,用户直接向客户端注册,客户端以自己的名义要求"服务提供商"提供服务,其实不存在授权问题。

(A)客户端向认证服务器进行身份认证,并要求一个访问令牌。

(B)认证服务器确认无误后,向客户端提供访问令牌。

2.4 更新令牌

令牌的有效期到了,OAuth2允许用户自动更新令牌。具体方法是,认证服务器颁发令牌的时候,一次性颁发两个令牌,一个用于获取数据,另一个用于获取新的令牌(refresh token 字段)。令牌到期前,用户使用 refresh token 发一个请求,去更新令牌。

2.5 JWT

1.Header

头部Header一般由2个部分组成alg和typ,alg是加密算法,如HMAC或SHA256,typ是token类型,取值为jwt,一个Header的例子
{
"alg": "HS256",
"typ": "JWT"
}
然后对Header部分进行Base64编码,得到第一部分的值

2.Payload

内容部分Payload是JWT存储信息的主体。JWT指定七个默认字段供选择。

iss:发行人

exp:到期时间

sub:主题

aud:用户

nbf:在此之前不可用

iat:发布时间

jti:JWT ID用于标识该JWT

除以上默认字段外,自定义的信息保存在该部分。然后对该部分的值进行Base64编码,得到第二部分的值。

3.Signature

签名哈希部分是对上面两部分数据签名,通过指定的算法生成哈希,以确保数据不会被篡改。包含三部分:
header (base64后的)、payload (base64后的)、secret(私钥)
这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加密,然后就构成了jwt的第三部分。

Java生成JWT【待完成】

三.oauth2项目【待完成】

3.1 基于内存储存的oauth2项目

创建认证服务器

在spring项目中引入spring security和spring oauth2依赖,因为oauth2包含security,因此引入oauth2就可以了

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-oauth2</artifactId><version>2.2.0.RELEASE</version>
</dependency>

main方法

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/*
创建认证服务器*/
@SpringBootApplication
public class Oauth2ServerApplication {public static void main(String[] args) {SpringApplication.run(Oauth2ServerApplication.class, args);}
}

创建config包,保存配置文件。
创建AuthorizationServerConfig配置一个客户端。使用BCryptPasswordEncoder进行对客户端secret加密。

package com.li.spring.cloud.oauth2.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
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;@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {@Beanpublic BCryptPasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}//配置客户端@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {// 配置客户端clients// 使用内存设置.inMemory()// client_id.withClient("client")// client_secret.secret(passwordEncoder().encode("secret"))// 授权类型.authorizedGrantTypes("authorization_code")// 授权范围.scopes("app")// 注册回调地址.redirectUris("http://localhost");}
}

@EnableAuthorizationServer注解开启认证服务。注入加密用的BCryptPasswordEncoder实例。然后,配置需要认证的客户端,
参数:client_id代表是哪个客户端也就是哪个APP或者web服务需要认证的;然后是客户端的secret秘钥需要加密,
authorizedGrantTypes授权方式指的是授权码,账户密码等,这里使用的是授权码(authorization_code),scopes表示范围,
redirectUris重定向地址。

认证之前需要先校验用户的账户密码是否正确,所以先配置WebSecurityConfig拦截(目前写死,后面改用数据库验证),配置了两个用户密码用了BCryptPasswordEncoder进行加密,不加密报错。

package com.li.spring.cloud.oauth2.config;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;@Configuration
@EnableWebSecurity
//对全部方法进行验证
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {@AutowiredBCryptPasswordEncoder passwordEncoder;//配置内存登录用户验证@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication().withUser("admin").password(passwordEncoder.encode("123456")).roles("ADMIN").and().withUser("user").password(passwordEncoder.encode("123456")).roles("USER");}}

然后就可以访问oauth2默认提供的接口获取授权码。

oauth2提供的默认端点URL/oauth/authorize:授权端点
/oauth/token:令牌端点
/oauth/confirm_access:用户确认授权提交端点
/oauth/error:授权服务错误信息端点
/oauth/check_token:用于资源服务访问的令牌解析端点
/oauth/token_key:提供公有密匙的端点,如果使用JWT令牌

启动项目,访问
http://localhost:8080/oauth/authorize?client_id=client&response_type=code

填写账号:admin和密码:123456

image

点击授权

得到地址:http://localhost/?code=GlwyCi

得到授权码:GlwyCi,通过授权码取token。

使用postman发送post请求取token
地址:http://client:secret@localhost:8080/oauth/token
按照Oauth2规范,Access token请求应该使用application/x-www-form-urlencoded,填写grant_type和code。
结果如下图:

image

获取到token:"f5f2b0c6-9f61-4d1d-94d1-21fb8e34fe37"

3.2 基于JDBC储存令牌

按照官方提供的数据库脚本创建创建数据表

CREATE TABLE `clientdetails` (`appId` varchar(128) NOT NULL,`resourceIds` varchar(256) DEFAULT NULL,`appSecret` varchar(256) DEFAULT NULL,`scope` varchar(256) DEFAULT NULL,`grantTypes` varchar(256) DEFAULT NULL,`redirectUrl` varchar(256) DEFAULT NULL,`authorities` varchar(256) DEFAULT NULL,`access_token_validity` int(11) DEFAULT NULL,`refresh_token_validity` int(11) DEFAULT NULL,`additionalInformation` varchar(4096) DEFAULT NULL,`autoApproveScopes` varchar(256) DEFAULT NULL,PRIMARY KEY (`appId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE `oauth_access_token` (`token_id` varchar(256) DEFAULT NULL,`token` blob,`authentication_id` varchar(128) NOT NULL,`user_name` varchar(256) DEFAULT NULL,`client_id` varchar(256) DEFAULT NULL,`authentication` blob,`refresh_token` varchar(256) DEFAULT NULL,PRIMARY KEY (`authentication_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE `oauth_approvals` (`userId` varchar(256) DEFAULT NULL,`clientId` varchar(256) DEFAULT NULL,`scope` varchar(256) DEFAULT NULL,`status` varchar(10) DEFAULT NULL,`expiresAt` timestamp NULL DEFAULT NULL,`lastModifiedAt` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE `oauth_client_details` (`client_id` varchar(128) NOT NULL,`resource_ids` varchar(256) DEFAULT NULL,`client_secret` varchar(256) DEFAULT NULL,`scope` varchar(256) DEFAULT NULL,`authorized_grant_types` varchar(256) DEFAULT NULL,`web_server_redirect_uri` varchar(256) DEFAULT NULL,`authorities` varchar(256) DEFAULT NULL,`access_token_validity` int(11) DEFAULT NULL,`refresh_token_validity` int(11) DEFAULT NULL,`additional_information` varchar(4096) DEFAULT NULL,`autoapprove` varchar(256) DEFAULT NULL,PRIMARY KEY (`client_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE `oauth_client_token` (`token_id` varchar(256) DEFAULT NULL,`token` blob,`authentication_id` varchar(128) NOT NULL,`user_name` varchar(256) DEFAULT NULL,`client_id` varchar(256) DEFAULT NULL,PRIMARY KEY (`authentication_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE `oauth_code` (`code` varchar(256) DEFAULT NULL,`authentication` blob
) ENGINE=InnoDB DEFAULT CHARSET=utf8;CREATE TABLE `oauth_refresh_token` (`token_id` varchar(256) DEFAULT NULL,`token` blob,`authentication` blob
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

插入一条客户端数据,secret是已经通过BCryptPasswordEncoder加密的数据。

insert into oauth_client_details (client_id, client_secret, scope, authorized_grant_types, web_server_redirect_uri)
value ('client', '$2a$10$agcHmHOgO7yHZBKNOBGlauHKwT3NoGRBmZe6TqsUJ1dXJhE465tbG','app','authorization_code','http://localhost');

在pom文件中引入连接数据库的jar包

<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.6</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency>

在application.xml中配置数据库信息

spring:profiles:active: devapplication:name: spring-oauth2-serverdatasource:url: jdbc:mysql://${mysqlServer.ip}:${mysqlServer.port}/spring_oauth2?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2b8&allowMultiQueries=trueusername: ${mysqlServer.name}password: ${mysqlServer.password}driver-class-name: com.mysql.jdbc.Driverdruid:initial-size: 3min-idle: 3max-active: 10max-wait: 60000stat-view-servlet:login-username: rootlogin-password: 1234filter:stat:log-slow-sql: trueslow-sql-millis: 2000type: com.alibaba.druid.pool.DruidDataSource
server:port: 8080

修改AuthorizationServerConfig类

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
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.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;import javax.sql.DataSource;@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {@Autowiredprivate DataSource dataSource;@Beanpublic TokenStore tokenStore() {// 令牌保存到数据return new JdbcTokenStore(dataSource);}@Beanpublic ClientDetailsService jdbcClientDetails() {// 配置客户端信息return new JdbcClientDetailsService(dataSource);}@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {// 设置令牌endpoints.tokenStore(tokenStore());}@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {// 读取客户端配置clients.withClientDetails(jdbcClientDetails());}
}

经测试,在数据表oauth_access_token中成功保存token信息。

3.3 资源服务配置

新建spring-cloud-oauth2-resource的spring boot项目,
在pom.xml中导入oauth2依赖

<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>

新建config包保存配置文件。新建ResourceServerConfig类配置资源服务。

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.ResourceServerConfigurerAdapter;@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {@Overridepublic void configure(HttpSecurity http) throws Exception {http.exceptionHandling().and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().authorizeRequests()// 以下为配置所需保护的资源路径及权限,需要与认证服务器配置的授权部分对应.antMatchers("/resource").authenticated();}
}

在该类中配置resource路径需要授权访问。

在application.yml中配置认证服务器

spring:application:name: spring-cloud-oauth2-resourcesecurity:oauth2:client:client-id: clientclient-secret: secretaccess-token-uri: http://localhost:8080/oauth/tokenuser-authorization-uri: http://localhost:8080/oauth/authorizeresource:token-info-uri: http://localhost:8080/oauth/check_tokenserver:port: 8081

因为resource需要oauth/check_token访问授权服务器验证token,所以在授权项目中排出/check_token接口。修改WebSecurityConfig类,

    //添加如下代码@Overridepublic void configure(WebSecurity web) throws Exception {web.ignoring().antMatchers("/oauth/check_token");}

创建测试ResourceTestController

package com.li.spring.cloud.oauth2.resource.controller;import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
public class ResourceTestController {@RequestMapping("resource")public String testResource() {return "资源测试";}@GetMapping("test")public String test() {return "不需要授权测试";}
}

当未携带令牌访问http://localhost:8081/test时可以访问,当访问http://localhost:8081/resource时拒绝访问

image

按照上文的方式获取到令牌:eb6df7b2-337d-4c0c-8be5-d692fdc9b5de。

携带令牌访问http://localhost:8081/resource?access_token=eb6df7b2-337d-4c0c-8be5-d692fdc9b5de,访问成功。

image

3.4 JWT令牌

上面的授权方法的问题是用户每次请求资源服务,资源服务都需要携带令牌访问认证服务去校验令牌的合法性,并根据令牌获取用户的相关信息,性能低下。

使用JWT的思路是,用户认证通过会得到一个JWT令牌,JWT令牌中已经包括了用户相关的信息,客户端只需要携带 JWT访问资源服务,资源服务根据事先约定的算法自行完成令牌校验,无需每次都请求认证服务完成授权。

JWT令牌的优点: 1、jwt基于json,非常方便解析。 2、可以在令牌中自定义丰富的内容,易扩展。 3、通过非对称加密算法及数字签名技术,JWT防止篡改,安全性高。 4、资源服务使用JWT可不依赖认证服务即可完成授权。 缺点: 1、JWT令牌较长,占存储空间比较大。

3.4.1 生成私钥和公钥

1.生成密钥证书

keytool -genkeypair -alias mykey -keyalg RSA -keypass vortex -keystore key.keystore -storepass vortex

keytools是Java提供的证书管理工具

-alias:密钥的别名

-keyalg:使用的hash算法

-keypass:密钥的访问密码

-keystore:密钥库文件名

-storepass:密钥库的访问密码

生成证书文件

2.导出公钥

下载openssl,安装并配置到环境变量

keytool -list -rfc --keystore key.keystore | openssl x509 -inform pem -pubkey

输入密钥库密码就可以得到公钥

公钥
-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo2UE2U3slFjFFedcMwxGrJdLfKCOfo2ptmm6brcQHfECQgSgtuCBpgtdiYmXWN4RHNyXE83ljXSjtOYp64QcyhfyvWpE+P13vg3qJDgVNdq3i6ZPZfgAlacHI92p8Im5Wy+NMaV4Tv8woNDC0tQjKBdVdGX8ev8W0W5CtNSPPWYw7GupG2OaSThxKnXa5GPFlfgsa6pmv+03G0QbuY7MyrXl/aItmi+l0j0vy7hqlHbjj2FpxPDXeTqNjThy5l3aBjh1kGv8K/j/9wDsKEPNRlminROR6Hj9+6FD9O8HM32malJmZmDu2tqwaXnilLZGFiSLKFeoH8WNKDRLhpUM8wIDAQAB-----END PUBLIC KEY-----

将证书文件放置在resource文件夹下,在test文件夹下测试jwt令牌生成及校验。

package com.li.oauth2.test;import org.springframework.core.io.ClassPathResource;
import org.springframework.security.jwt.Jwt;
import org.springframework.security.jwt.JwtHelper;
import org.springframework.security.jwt.crypto.sign.RsaSigner;
import org.springframework.security.jwt.crypto.sign.RsaVerifier;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;import java.security.KeyPair;
import java.security.interfaces.RSAPrivateKey;public class TestJwt {public static void main(String[] args) {//证书文件String keystore = "key.keystore";//密钥库密码String keystorePassword = "vortex";//密钥别名String alias = "mykey";//密钥的密码String keyPassword = "vortex";ClassPathResource resource = new ClassPathResource(keystore);//密钥工厂KeyStoreKeyFactory factory = new KeyStoreKeyFactory(resource, keystorePassword.toCharArray());//密钥对KeyPair keyPair = factory.getKeyPair(alias, keyPassword.toCharArray());//私钥RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();Jwt token = JwtHelper.encode("姓名:lijianyou", new RsaSigner(privateKey));String encode = token.getEncoded();System.out.println(encode);//公钥String publicKey = "-----BEGIN PUBLIC KEY-----MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo2UE2U3slFjFFedcMwxGrJdLfKCOfo2ptmm6brcQHfECQgSgtuCBpgtdiYmXWN4RHNyXE83ljXSjtOYp64QcyhfyvWpE+P13vg3qJDgVNdq3i6ZPZfgAlacHI92p8Im5Wy+NMaV4Tv8woNDC0tQjKBdVdGX8ev8W0W5CtNSPPWYw7GupG2OaSThxKnXa5GPFlfgsa6pmv+03G0QbuY7MyrXl/aItmi+l0j0vy7hqlHbjj2FpxPDXeTqNjThy5l3aBjh1kGv8K/j/9wDsKEPNRlminROR6Hj9+6FD9O8HM32malJmZmDu2tqwaXnilLZGFiSLKFeoH8WNKDRLhpUM8wIDAQAB-----END PUBLIC KEY-----";//校验令牌Jwt jwt = JwtHelper.decodeAndVerify(encode, new RsaVerifier(publicKey));System.out.println(jwt.getClaims());}
}

测试成功。

修改AuthorizationServerConfig配置类

package com.li.spring.cloud.oauth2.config;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
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.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;import javax.sql.DataSource;@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {@Autowiredprivate UserDetailsService userDetailsService;@Autowiredprivate AuthenticationManager authenticationManager;@Autowired@Qualifier("dataSource")private DataSource dataSource;@Bean("jdbcTokenStore")public JdbcTokenStore getJdbcTokenStore() {return new JdbcTokenStore(dataSource);}@Bean("jdbcClientDetailsService")public JdbcClientDetailsService getJdbcClientDetailsService() {return new JdbcClientDetailsService(dataSource);}@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {// 使用JdbcClientDetailsService客户端详情服务clients.withClientDetails(getJdbcClientDetailsService());}@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) {endpoints.authenticationManager(authenticationManager)// 配置JwtAccessToken转换器.accessTokenConverter(jwtAccessTokenConverter())// refresh_token需要userDetailsService.reuseRefreshTokens(false).userDetailsService(userDetailsService);//.tokenStore(getJdbcTokenStore());}@Overridepublic void configure(AuthorizationServerSecurityConfigurer oauthServer) {oauthServer.tokenKeyAccess("permitAll()")// 开启/oauth/check_token验证端口认证权限访问.checkTokenAccess("isAuthenticated()");}/*** 使用非对称加密算法来对Token进行签名* @return*/@Beanpublic JwtAccessTokenConverter jwtAccessTokenConverter() {//TODO 自定义converter,将用户信息保存到tokenfinal JwtAccessTokenConverter converter = new JwtAccessTokenConverter();// 导入证书KeyStoreKeyFactory keyStoreKeyFactory =new KeyStoreKeyFactory(new ClassPathResource("key.keystore"), "vortex".toCharArray());converter.setKeyPair(keyStoreKeyFactory.getKeyPair("mykey"));return converter;}
}

实现UserDetails接口

package com.li.spring.cloud.oauth2.domain;import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;import java.util.Collection;public class MyUserDetails implements UserDetails {private String userName;private String password;private User user;public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public void setPassword(String password) {this.password = password;}public User getUser() {return user;}public void setUser(User user) {this.user = user;}/*** 重写getAuthorities方法,将用户的角色作为权限*/@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {//TODO 后续带完善return AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_SUPER");}@Overridepublic String getPassword() {return password;}@Overridepublic String getUsername() {return userName;}@Overridepublic boolean isAccountNonExpired() {return true;}@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}

自定义实现UserDetailsService,覆写loadUserByUsername方法

package com.li.spring.cloud.oauth2.service.impl;import com.li.spring.cloud.oauth2.domain.MyUserDetails;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.stereotype.Service;@Primary
@Service
public class UserDetailsServiceImpl implements UserDetailsService {@AutowiredClientDetailsService clientDetailsService;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//TODO 判断用户是否是admin,(后面改从数据库查用户)if (StringUtils.isEmpty(username) || !"admin".equals(username)) {throw new UsernameNotFoundException("用户不存在");}MyUserDetails userDetails = new MyUserDetails();userDetails.setUserName(username);userDetails.setPassword("123456");return userDetails;}
}

在WebSecurityConfig配置类中覆写AuthenticationManager并配置成bean

package com.li.spring.cloud.oauth2.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;@Configuration
@EnableWebSecurity
//对全部方法进行验证
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Beanpublic BCryptPasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}//配置内存登录用户验证@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.inMemoryAuthentication().withUser("admin").password(passwordEncoder().encode("123456")).roles("ADMIN").and().withUser("user").password(passwordEncoder().encode("123456")).roles("USER");}@Overridepublic void configure(WebSecurity web) throws Exception {web.ignoring().antMatchers("/oauth/check_token");}@Override@Beanpublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}}

启动项目
访问http://localhost:8080/oauth/authorize?client_id=client&response_type=code
得到授权码:J3rnGY

用postman发送post请求获取token

token获取

得到token

eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1ODIxNDcxNzksInVzZXJfbmFtZSI6ImFkbWluIiwiYXV0aG9yaXRpZXMiOlsiUk9MRV9BRE1JTiJdLCJqdGkiOiJhMDM5ZDIyNC03MTU3LTRhYzEtOWY5NS0wNDA5ODVkYzY0ZWUiLCJjbGllbnRfaWQiOiJjbGllbnQiLCJzY29wZSI6WyJhcHAiXX0.hETFtOSz8k2KM1r7hl5LI2Zr5PPO0jUZ6-m9HcJ1EfFJ9tRU-d26PNm6e_pT9-ZqtfvKRKkEiustY7YmUdosJlca5OFDxDX5R8LZwlSAAGkOs0rS43BlkLRwpNLfiemzhBjbZIMT8FL5MTLHo26bck856LoOuUuHaC9wanP4677wF43VSKiH2nSY0NaJy67vRXnj9bnAzpi9e34ztcTqDypil0WkAE1bHr0ihqocmpC_PnCzimKab3lb6ICeSpArkYnIPiBHHBm_6k0KscXiayexOul37Yl3GGOYS_kfjcSHCs0W-sUnaPjbUey4IXpSYrq1nt1bJMxDETt0dP0Qug

将上文得到的公钥保存到publickey.txt文件夹中,放在资源服务项目的resource文件夹中,修改ResourceServerConfig配置类

package com.li.spring.cloud.oauth2.resource.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
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.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.stream.Collectors;@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {//公钥private static final String PUBLIC_KEY = "publickey.txt";//定义JwtTokenStore,使用jwt令牌@Beanpublic TokenStore tokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) {return new JwtTokenStore(jwtAccessTokenConverter);}//定义JJwtAccessTokenConverter,使用jwt令牌@Beanpublic JwtAccessTokenConverter jwtAccessTokenConverter() {JwtAccessTokenConverter converter = new JwtAccessTokenConverter();converter.setVerifierKey(getPubKey());return converter;}/*** 获取非对称加密公钥 Key* @return 公钥 Key*/private String getPubKey() {Resource resource = new ClassPathResource(PUBLIC_KEY);try {InputStreamReader inputStreamReader = newInputStreamReader(resource.getInputStream());BufferedReader br = new BufferedReader(inputStreamReader);return br.lines().collect(Collectors.joining("\n"));} catch (IOException ioe) {return null;}}//Http安全配置,对每个到达系统的http请求链接进行校验@Overridepublic void configure(HttpSecurity http) throws Exception {//        http.authorizeRequests().anyRequest().authenticated(); //所有请求必须认证通过//所有请求必须认证通过 (swagger-ui放行)http.authorizeRequests()//下边的路径放行.antMatchers("/swagger-ui.html").permitAll().anyRequest().authenticated();}
}

启动项目测试,填写token,测试成功

测试成功图片

修改token,测试失败

测试失败图片

3.5 修改项目(使用公司spring版本)

单点登录:单点登录全称Single Sign On,是指在多系统应用群中登录一个系统,便可在其他所有系统中得到授权而无需再次登录,包括单点登录与单点注销两部分。

用户访问系统1的受保护资源,系统1发现用户未登录,跳转至sso认证中心,并将自己的地址作为参数

sso认证中心发现用户未登录,将用户引导至登录页面
用户输入用户名密码提交登录申请

sso认证中心校验用户信息,创建用户与sso认证中心之间的会话,称为全局会话,同时创建授权令牌

sso认证中心带着令牌跳转会最初的请求地址(系统1)

系统1拿到令牌,去sso认证中心校验令牌是否有效

sso认证中心校验令牌,返回有效,注册系统1

系统1使用该令牌创建与用户的会话,称为局部会话,返回受保护资源

用户访问系统2的受保护资源

系统2发现用户未登录,跳转至sso认证中心,并将自己的地址作为参数

sso认证中心发现用户已登录,跳转回系统2的地址,并附上令牌

系统2拿到令牌,去sso认证中心校验令牌是否有效

sso认证中心校验令牌,返回有效,注册系统2

系统2使用该令牌创建与用户的局部会话,返回受保护资源

3.5.1 修改项目

主要修改:

  1. 去除BCryptPasswordEncoder加密,spring 1.5版本不支持客户端secret和用户密码加密
  2. 注释掉JWT令牌部分,改用oauth2原生令牌
  3. 加入redis保存令牌(目前项目中注释掉)
  4. 采用密码模式测试项目(不改动项目代码,在数据库客户端表中给客户端加密码模式)

AuthorizationServerConfig认证服务器配置

package com.vortex.cloud.config;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
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.client.JdbcClientDetailsService;
import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;
import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;import javax.sql.DataSource;@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {@Autowiredprivate UserDetailsService userDetailsService;@Autowiredprivate AuthenticationManager authenticationManager;@Autowiredprivate RedisConnectionFactory redisConnectionFactory;@Autowired@Qualifier("dataSource")private DataSource dataSource;@Bean("jdbcTokenStore")public JdbcTokenStore getJdbcTokenStore() {return new JdbcTokenStore(dataSource);}@Bean("redisTokenStore")public RedisTokenStore redisTokenStore() {return new RedisTokenStore(redisConnectionFactory);}@Bean("jdbcClientDetailsService")public JdbcClientDetailsService getJdbcClientDetailsService() {return new JdbcClientDetailsService(dataSource);}@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {// 使用JdbcClientDetailsService客户端详情服务clients.withClientDetails(getJdbcClientDetailsService());}@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) {endpoints.authenticationManager(authenticationManager)// 配置Jwt令牌转换器//.accessTokenConverter(jwtAccessTokenConverter())// refresh_token需要userDetailsService.reuseRefreshTokens(false).userDetailsService(userDetailsService)//配置redis储存令牌//.tokenStore(redisTokenStore())//配置jdbc储存令牌.tokenStore(getJdbcTokenStore());}@Overridepublic void configure(AuthorizationServerSecurityConfigurer oauthServer) {oauthServer.tokenKeyAccess("permitAll()")// 开启/oauth/check_token验证端口认证权限访问.checkTokenAccess("isAuthenticated()").allowFormAuthenticationForClients();}/*@Beanpublic JwtAccessTokenConverter jwtAccessTokenConverter() {//TODO 自定义converter,将用户信息保存到tokenfinal JwtAccessTokenConverter converter = new JwtAccessTokenConverter();// 导入证书KeyStoreKeyFactory keyStoreKeyFactory =new KeyStoreKeyFactory(new ClassPathResource("key.keystore"), "vortex".toCharArray());converter.setKeyPair(keyStoreKeyFactory.getKeyPair("mykey"));return converter;}*/
}

WebSecurityConfig安全配置类,使用userDetailsService校验用户数据(目前在userDetailsService类中写死,后面改从数据库查询)

package com.vortex.cloud.config;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.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;@Configuration
@EnableWebSecurity
//对全部方法进行验证
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate UserDetailsService userDetailsService;@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService);}@Overridepublic void configure(WebSecurity web) throws Exception {web.ignoring().antMatchers("/oauth/check_token");}@Override@Beanpublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}}

UserDetailsServiceImpl类从数据库校验用户信息(目前写死)

package com.vortex.cloud.service.impl;import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Primary;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.stereotype.Service;import java.util.ArrayList;
import java.util.List;@Primary
@Service
public class UserDetailsServiceImpl implements UserDetailsService {@AutowiredClientDetailsService clientDetailsService;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//TODO 判断用户是否是admin,(后面改从数据库查用户)if (StringUtils.isEmpty(username) || !"admin".equals(username)) {throw new UsernameNotFoundException("用户不存在");}String role = "ADMIN";List<SimpleGrantedAuthority> authorities = new ArrayList<>();authorities.add(new SimpleGrantedAuthority(role));return new User(username, "123456", authorities);}
}

资源服务配置(注释掉JWT令牌校验部分)

package com.vortex.cloud.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.stream.Collectors;@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {//JWT令牌配置
/*//公钥private static final String PUBLIC_KEY = "publickey.txt";//定义JwtTokenStore,使用jwt令牌@Beanpublic TokenStore tokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) {return new JwtTokenStore(jwtAccessTokenConverter);}//定义JJwtAccessTokenConverter,使用jwt令牌@Beanpublic JwtAccessTokenConverter jwtAccessTokenConverter() {JwtAccessTokenConverter converter = new JwtAccessTokenConverter();converter.setVerifierKey(getPubKey());return converter;}//获取非对称加密公钥 Keyprivate String getPubKey() {Resource resource = new ClassPathResource(PUBLIC_KEY);try {InputStreamReader inputStreamReader = newInputStreamReader(resource.getInputStream());BufferedReader br = new BufferedReader(inputStreamReader);return br.lines().collect(Collectors.joining("\n"));} catch (IOException ioe) {return null;}}*///Http安全配置,对每个到达系统的http请求链接进行校验@Overridepublic void configure(HttpSecurity http) throws Exception {//        http.authorizeRequests().anyRequest().authenticated(); //所有请求必须认证通过//所有请求必须认证通过 (swagger-ui放行)http.authorizeRequests()//下边的路径放行.antMatchers("/swagger-ui.html").permitAll().anyRequest().authenticated();}
}

资源服务项目配置(application.yml)

spring:application:name: spring-cloud-oauth2-resourcesecurity:oauth2:client:client-id: clientclient-secret: secretaccess-token-uri: http://localhost:8080/oauth/tokenuser-authorization-uri: http://localhost:8080/oauth/authorizeresource:token-info-uri: http://localhost:8080/oauth/check_tokenserver:port: 8081

数据库中插入新客户端信息

INSERT INTO oauth_client_details(client_id, client_secret, scope, authorized_grant_types,web_server_redirect_uri, authorities, access_token_validity,refresh_token_validity, additional_information, autoapprove)
VALUES('client1', 'secret', 'all','authorization_code,refresh_token,password', null, null, 3600, 36000, null, true);

经测试,授权码模式成功。

测试密码模式:

使用postman访问http://localhost:8080/oauth/token
成功获得token

image

使用token从资源服务器获取数据成功

image.png
image.png
image.png

spring-cloud-oauth2相关推荐

  1. 《深入理解 Spring Cloud 与微服务构建》第十七章 使用 Spring Cloud OAuth2 保护微服务系统

    <深入理解 Spring Cloud 与微服务构建>第十七章 使用 Spring Cloud OAuth2 保护微服务系统 文章目录 <深入理解 Spring Cloud 与微服务构 ...

  2. Spring Cloud OAuth2中访问/oauth/token报Unsupported grant type: password问题的解决

    Spring Cloud OAuth2中访问/oauth/token报Unsupported grant type: password问题的解决 问题分析 问题解决 问题分析 在新建的Spring C ...

  3. java调用授权接口oauth2_微信授权就是这个原理,Spring Cloud OAuth2 授权码模式

    上一篇文章Spring Cloud OAuth2 实现单点登录介绍了使用 password 模式进行身份认证和单点登录.本篇介绍 Spring Cloud OAuth2 的另外一种授权模式-授权码模式 ...

  4. 面试官:能说一说微信授权的原理吗?(Spring Cloud OAuth2 授权码模式)

    我是风筝,公众号「古时的风筝」,一个简单的程序员鼓励师. 文章会收录在 JavaNewBee 中,更有 Java 后端知识图谱,从小白到大牛要走的路都在里面. 上一篇文章Spring Cloud OA ...

  5. spring cloud oauth2系列篇(二)深入authorization_code授权码模式完整实现

    项目的源码地址:https://github.com/daxian-zhu/online_edu 项目是最新的,文章可能不是最新的 这里先简单的介绍项目,不然有的地方可能我表达不清楚容易造成误解 on ...

  6. Spring Cloud OAuth2 认证服务器

    1.Spring Cloud OAuth2介绍 Spring Cloud OAuth2 是 Spring Cloud 体系对OAuth2协议的实现,可以用来做多个微服务的统一认证(验证身份合法性)授权 ...

  7. spring cloud OAuth2

    spring cloud OAuth2 1. 为什么需要oauth 2. oauth的原理 3. oauth的组成 3.1 OAuth2 Provider 3.1.1 授权服务Authorizatio ...

  8. Spring Cloud OAuth2 JWT 微服务认证服务器得构建

    文章目录 Spring Cloud OAuth2 JWT 微服务认证服务器得构建 前言 认证服务得搭建 `AuthorizationServer` `WebSecurityConfig` `Autho ...

  9. Spring Cloud OAuth2中访问/oauth/token报invalid_client问题的解决

    Spring Cloud OAuth2中访问/oauth/token报invalid_client问题的解决 问题分析 问题解决 问题分析 初建Spring Cloud OAuth2项目中访问获取ac ...

  10. 中国重汽微服务管理_干货 | 微服务架构下 Spring Cloud OAuth2 通用权限管理系统

    点击蓝色"泥瓦匠BYSocket",关注我哟 加个"星标",不忘文末签到哦 作者:王杰 项目地址 Gitee: https://gitee.com/log4j/ ...

最新文章

  1. 判断控件是否出现了滚动条
  2. DIV的id和class
  3. win7 ghost 安装串口驱动inf文件出现问题
  4. 复盘-电商产品「分类」功能迭代
  5. 注意力机制中的Q、K和V的意义
  6. kafka2.5.0创建主题topic命令
  7. solr中的ik分词器的原理是什么
  8. 【数据结构与算法】之树的概念与使用
  9. python面向对象三大基本特性_python面向对象之三大特性
  10. .NET 数据库缓存依赖策略实现
  11. python tkinter Listbox用法
  12. 深度学习——夏侯南溪关注的深度学习任务
  13. 【转载】三方框架整理
  14. php实现电脑自动关机,如何设置定时关电脑?三种方法教你设置电脑自动关机
  15. 出现APK安装包解析错误问题说明
  16. css样式中的vw什么意思,css vw是什么单位
  17. Springboot母婴店购物系统9j5v8计算机毕业设计-课程设计-期末作业-毕设程序代做
  18. 沐神《动手学深度实战Kaggle比赛:狗的品种识别(ImageNet Dogs)
  19. 启动异常 Field XXX in XXXX required a bean of type XXXX that could not be found.
  20. 阿里天池-全球数据智能大赛

热门文章

  1. 那些花儿(吉他版) --朴树
  2. jQuery选择器详细介绍
  3. 大数据的核心架构层是哪些?
  4. 要求用缓冲流:有如下字符串“If you want to change your fate I think you must come to the dark horse to learn java“
  5. Java 动态眨眼 EyesJPanel (整理)
  6. 一年成为Emacs高手 (像神一样使用编辑器)
  7. POJ - 2142 扩展欧几里得
  8. 爱了爱了!ALIENWAER外星人AW410K机械键盘Cherry茶轴RGB灯光全键无冲!免费包邮送到家!...
  9. c语言双竖线,机器学习中的双竖线
  10. 台式计算机摄像头插哪,台式电脑摄像头怎么调试