使用Spring Boot 2通过OAuth2和JWT进行集中授权

本指南逐步介绍了使用Spring Boot 2创建集中式身份验证和授权服务器的过程,还将提供演示资源服务器。

如果您不熟悉OAuth2,建议您阅读此书。

先决条件

  • JDK 1.8
  • 文本编辑器或您喜欢的IDE
  • Maven 3.0+

实施概述

对于这个项目,我们将通过Spring Boot使用Spring Security 5 。 如果您熟悉早期版本,那么《 Spring Boot迁移指南》可能会有用。

OAuth2术语

  • 资源所有者
    • 授权应用程序访问其帐户的用户。
  • 资源服务器
    • client获取access token之后处理已认证请求的服务器。
  • 客户
    • 代表资源所有者访问受保护资源的应用程序。
  • 授权服务器
    • 在成功验证clientresource owner并授权请求之后,发出访问令牌的服务器。
  • 访问令牌
    • 用于访问受保护资源的唯一令牌
  • 范围
    • 许可
  • 智威汤逊
    • JSON Web令牌是一种用于在RFC 7519中定义的在双方之间安全地表示声明的方法
  • 赠款类型
    • grant是一种获取访问令牌的方法。

授权服务器

为了构建我们的Authorization Server我们将通过Spring Boot 2.1.x使用Spring Security5.x 。

依存关系

您可以转到start.spring.io并生成一个新项目,然后添加以下依赖项:

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><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.2.RELEASE</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><dependency><groupId>com.h2database</groupId><artifactId>h2</artifactId><scope>runtime</scope></dependency>		</dependencies>

数据库

在本教程中,我们将使用H2数据库 。
在这里,您可以找到Spring Security所需的参考OAuth2 SQL模式。

CREATE TABLE IF NOT EXISTS oauth_client_details (client_id VARCHAR(256) PRIMARY KEY,resource_ids VARCHAR(256),client_secret VARCHAR(256) NOT NULL,scope VARCHAR(256),authorized_grant_types VARCHAR(256),web_server_redirect_uri VARCHAR(256),authorities VARCHAR(256),access_token_validity INTEGER,refresh_token_validity INTEGER,additional_information VARCHAR(4000),autoapprove VARCHAR(256)
);CREATE TABLE IF NOT EXISTS oauth_client_token (token_id VARCHAR(256),token BLOB,authentication_id VARCHAR(256) PRIMARY KEY,user_name VARCHAR(256),client_id VARCHAR(256)
);CREATE TABLE IF NOT EXISTS oauth_access_token (token_id VARCHAR(256),token BLOB,authentication_id VARCHAR(256),user_name VARCHAR(256),client_id VARCHAR(256),authentication BLOB,refresh_token VARCHAR(256)
);CREATE TABLE IF NOT EXISTS oauth_refresh_token (token_id VARCHAR(256),token BLOB,authentication BLOB
);CREATE TABLE IF NOT EXISTS oauth_code (code VARCHAR(256), authentication BLOB
);

注意:由于本教程使用JWT并非所有表都是必需的。

然后添加以下条目

-- The encrypted client_secret it `secret`
INSERT INTO oauth_client_details (client_id, client_secret, scope, authorized_grant_types, authorities, access_token_validity)VALUES ('clientId', '{bcrypt}$2a$10$vCXMWCn7fDZWOcLnIEhmK.74dvK1Eh8ae2WrWlhr2ETPLoxQctN4.', 'read,write', 'password,refresh_token,client_credentials', 'ROLE_CLIENT', 300);

上面的client_secret是使用bcrypt生成的。
前缀{bcrypt}是必需的,因为我们将使用Spring Security 5.x的DelegatingPasswordEncoder的新功能。

在下面的页面中,您可以找到Spring的org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl使用的UserAuthority参考SQL模式。

CREATE TABLE IF NOT EXISTS users (id INT AUTO_INCREMENT PRIMARY KEY,username VARCHAR(256) NOT NULL,password VARCHAR(256) NOT NULL,enabled TINYINT(1),UNIQUE KEY unique_username(username)
);CREATE TABLE IF NOT EXISTS authorities (username VARCHAR(256) NOT NULL,authority VARCHAR(256) NOT NULL,PRIMARY KEY(username, authority)
);

与之前相同,为用户及其权限添加以下条目。

-- The encrypted password is `pass`
INSERT INTO users (id, username, password, enabled) VALUES (1, 'user', '{bcrypt}$2a$10$cyf5NfobcruKQ8XGjUJkEegr9ZWFqaea6vjpXWEaSqTa2xL9wjgQC', 1);
INSERT INTO authorities (username, authority) VALUES ('user', 'ROLE_USER');

Spring安全配置

添加以下Spring配置类。

import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.core.userdetails.jdbc.JdbcDaoImpl;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;import javax.sql.DataSource;@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {private final DataSource dataSource;private PasswordEncoder passwordEncoder;private UserDetailsService userDetailsService;public WebSecurityConfiguration(final DataSource dataSource) {this.dataSource = dataSource;}@Overrideprotected void configure(final AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());}@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}@Beanpublic PasswordEncoder passwordEncoder() {if (passwordEncoder == null) {passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();}return passwordEncoder;}@Beanpublic UserDetailsService userDetailsService() {if (userDetailsService == null) {userDetailsService = new JdbcDaoImpl();((JdbcDaoImpl) userDetailsService).setDataSource(dataSource);}return userDetailsService;}}

引用Spring Blog :

@EnableWebSecurity批注和WebSecurityConfigurerAdapter一起提供基于Web的安全性。

如果您使用的是Spring Boot,则将自动配置DataSource对象,您可以将其注入到类中,而不必自己定义。 需要将其注入到UserDetailsService中,该服务将使用Spring Security提供的JdbcDaoImpl ,如有必要,您可以将其替换为自己的实现。

由于某些自动配置的Spring @Bean需要Spring Security的AuthenticationManager因此有必要重写authenticationManagerBean方法,并以@Bean authenticationManagerBean注释。

PasswordEncoder将由PasswordEncoderFactories.createDelegatingPasswordEncoder()处理,其中基于前缀处理一些密码编码器和委托,在我们的示例中,我们使用{bcrypt}作为密码的前缀。

授权服务器配置

授权服务器会验证clientuser凭据并提供令牌,在本教程中,我们将生成JSON Web Tokens aka JWT

为了对生成的JWT令牌进行签名,我们将使用自签名证书,并在使用Spring Configuration开始之前这样做,让我们创建一个@ConfigurationProperties类来绑定我们的配置属性。

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.io.Resource;@ConfigurationProperties("security")
public class SecurityProperties {private JwtProperties jwt;public JwtProperties getJwt() {return jwt;}public void setJwt(JwtProperties jwt) {this.jwt = jwt;}public static class JwtProperties {private Resource keyStore;private String keyStorePassword;private String keyPairAlias;private String keyPairPassword;public Resource getKeyStore() {return keyStore;}public void setKeyStore(Resource keyStore) {this.keyStore = keyStore;}public String getKeyStorePassword() {return keyStorePassword;}public void setKeyStorePassword(String keyStorePassword) {this.keyStorePassword = keyStorePassword;}public String getKeyPairAlias() {return keyPairAlias;}public void setKeyPairAlias(String keyPairAlias) {this.keyPairAlias = keyPairAlias;}public String getKeyPairPassword() {return keyPairPassword;}public void setKeyPairPassword(String keyPairPassword) {this.keyPairPassword = keyPairPassword;}}
}

添加以下Spring配置类。

import com.marcosbarbero.lab.sec.oauth.jwt.config.props.SecurityProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.password.PasswordEncoder;
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.ClientDetailsService;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
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 org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;import javax.sql.DataSource;
import java.security.KeyPair;@Configuration
@EnableAuthorizationServer
@EnableConfigurationProperties(SecurityProperties.class)
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {private final DataSource dataSource;private final PasswordEncoder passwordEncoder;private final AuthenticationManager authenticationManager;private final SecurityProperties securityProperties;private JwtAccessTokenConverter jwtAccessTokenConverter;private TokenStore tokenStore;public AuthorizationServerConfiguration(final DataSource dataSource, final PasswordEncoder passwordEncoder,final AuthenticationManager authenticationManager, final SecurityProperties securityProperties) {this.dataSource = dataSource;this.passwordEncoder = passwordEncoder;this.authenticationManager = authenticationManager;this.securityProperties = securityProperties;}@Beanpublic TokenStore tokenStore() {if (tokenStore == null) {tokenStore = new JwtTokenStore(jwtAccessTokenConverter());}return tokenStore;}@Beanpublic DefaultTokenServices tokenServices(final TokenStore tokenStore,final ClientDetailsService clientDetailsService) {DefaultTokenServices tokenServices = new DefaultTokenServices();tokenServices.setSupportRefreshToken(true);tokenServices.setTokenStore(tokenStore);tokenServices.setClientDetailsService(clientDetailsService);tokenServices.setAuthenticationManager(this.authenticationManager);return tokenServices;}@Beanpublic JwtAccessTokenConverter jwtAccessTokenConverter() {if (jwtAccessTokenConverter != null) {return jwtAccessTokenConverter;}SecurityProperties.JwtProperties jwtProperties = securityProperties.getJwt();KeyPair keyPair = keyPair(jwtProperties, keyStoreKeyFactory(jwtProperties));jwtAccessTokenConverter = new JwtAccessTokenConverter();jwtAccessTokenConverter.setKeyPair(keyPair);return jwtAccessTokenConverter;}@Overridepublic void configure(final ClientDetailsServiceConfigurer clients) throws Exception {clients.jdbc(this.dataSource);}@Overridepublic void configure(final AuthorizationServerEndpointsConfigurer endpoints) {endpoints.authenticationManager(this.authenticationManager).accessTokenConverter(jwtAccessTokenConverter()).tokenStore(tokenStore());}@Overridepublic void configure(final AuthorizationServerSecurityConfigurer oauthServer) {oauthServer.passwordEncoder(this.passwordEncoder).tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");}private KeyPair keyPair(SecurityProperties.JwtProperties jwtProperties, KeyStoreKeyFactory keyStoreKeyFactory) {return keyStoreKeyFactory.getKeyPair(jwtProperties.getKeyPairAlias(), jwtProperties.getKeyPairPassword().toCharArray());}private KeyStoreKeyFactory keyStoreKeyFactory(SecurityProperties.JwtProperties jwtProperties) {return new KeyStoreKeyFactory(jwtProperties.getKeyStore(), jwtProperties.getKeyStorePassword().toCharArray());}
}

在上面的类中,您将找到JWT所需的所有Spring @Bean 。 最重要的@Bean是: JwtAccessTokenConverterJwtTokenStoreDefaultTokenServices

JwtAccessTokenConverter使用自签名证书对生成的令牌进行签名。
JwtTokenStore实现仅从令牌本身读取数据。 并不是真正的商店,因为它从不持久化任何东西,它使用JwtAccessTokenConverter生成和读取令牌。
DefaultTokenServices使用TokenStore来保留令牌。

按照本指南生成自签名证书 。

生成自签名证书后,在application.yml上配置它。

security:jwt:key-store: classpath:keystore.jkskey-store-password: letmeinkey-pair-alias: mytestkeykey-pair-password: changeme

资源服务器配置

资源服务器托管HTTP资源 ,其中的HTTP资源可以是文档,照片或其他内容,在我们的情况下,它将是受OAuth2保护的REST API。

依存关系

<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><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.2.RELEASE</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-configuration-processor</artifactId><optional>true</optional></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.6</version></dependency>                </dependencies>

定义我们受保护的API

下面的代码定义了返回/me Principal的端点/me ,它要求经过身份验证的用户具有ROLE_USER的访问权限。

import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.security.Principal;@RestController
@RequestMapping("/me")
public class UserController {@GetMapping@PreAuthorize("hasRole('ROLE_USER')")public ResponseEntity<Principal> get(final Principal principal) {return ResponseEntity.ok(principal);}}

@PreAuthorize批注会在执行代码之前验证用户是否具有给定角色,以使其正常工作,有必要启用prePost批注,为此添加以下类:

import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfiguration {}

这里的重要部分是@EnableGlobalMethodSecurity(prePostEnabled = true)批注, prePostEnabled标志默认情况下设置为false

资源服务器配置

解码JWT令牌就必须使用public key从自签名的认证上使用的授权服务器签署的道理,做让我们先创建一个@ConfigurationProperties类绑定配置属性。

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.io.Resource;@ConfigurationProperties("security")
public class SecurityProperties {private JwtProperties jwt;public JwtProperties getJwt() {return jwt;}public void setJwt(JwtProperties jwt) {this.jwt = jwt;}public static class JwtProperties {private Resource publicKey;public Resource getPublicKey() {return publicKey;}public void setPublicKey(Resource publicKey) {this.publicKey = publicKey;}}}

使用以下命令从生成的JKS导出public key

$ keytool -list -rfc --keystore keystore.jks | openssl x509 -inform pem -pubkey -noout

样本响应如下所示:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAmWI2jtKwvf0W1hdMdajc
h+mFx9FZe3CZnKNvT/d0+2O6V1Pgkz7L2FcQx2uoV7gHgk5mmb2MZUsy/rDKj0dM
fLzyXqBcCRxD6avALwu8AAiGRxe2dl8HqIHyo7P4R1nUaea1WCZB/i7AxZNAQtcC
cSvMvF2t33p3vYXY6SqMucMD4yHOTXexoWhzwRqjyyC8I8uCYJ+xIfQvaK9Q1RzK
Rj99IRa1qyNgdeHjkwW9v2Fd4O/Ln1Tzfnk/dMLqxaNsXPw37nw+OUhycFDPPQF/
H4Q4+UDJ3ATf5Z2yQKkUQlD45OO2mIXjkWprAmOCi76dLB2yzhCX/plGJwcgb8XH
EQIDAQAB
-----END PUBLIC KEY-----

将其复制到public.txt文件,并将其放置在/src/main/resources ,然后配置指向该文件的application.yml

security:jwt:public-key: classpath:public.txt

现在,让我们为资源服务器添加Spring的配置。

import org.apache.commons.io.IOUtils;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
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.IOException;import static java.nio.charset.StandardCharsets.UTF_8;@Configuration
@EnableResourceServer
@EnableConfigurationProperties(SecurityProperties.class)
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {private static final String ROOT_PATTERN = "/**";private final SecurityProperties securityProperties;private TokenStore tokenStore;public ResourceServerConfiguration(final SecurityProperties securityProperties) {this.securityProperties = securityProperties;}@Overridepublic void configure(final ResourceServerSecurityConfigurer resources) {resources.tokenStore(tokenStore());}@Overridepublic void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers(HttpMethod.GET, ROOT_PATTERN).access("#oauth2.hasScope('read')").antMatchers(HttpMethod.POST, ROOT_PATTERN).access("#oauth2.hasScope('write')").antMatchers(HttpMethod.PATCH, ROOT_PATTERN).access("#oauth2.hasScope('write')").antMatchers(HttpMethod.PUT, ROOT_PATTERN).access("#oauth2.hasScope('write')").antMatchers(HttpMethod.DELETE, ROOT_PATTERN).access("#oauth2.hasScope('write')");}@Beanpublic DefaultTokenServices tokenServices(final TokenStore tokenStore) {DefaultTokenServices tokenServices = new DefaultTokenServices();tokenServices.setTokenStore(tokenStore);return tokenServices;}@Beanpublic TokenStore tokenStore() {if (tokenStore == null) {tokenStore = new JwtTokenStore(jwtAccessTokenConverter());}return tokenStore;}@Beanpublic JwtAccessTokenConverter jwtAccessTokenConverter() {JwtAccessTokenConverter converter = new JwtAccessTokenConverter();converter.setVerifierKey(getPublicKeyAsString());return converter;}private String getPublicKeyAsString() {try {return IOUtils.toString(securityProperties.getJwt().getPublicKey().getInputStream(), UTF_8);} catch (IOException e) {throw new RuntimeException(e);}}}

此配置的重要部分是三个@BeanJwtAccessTokenConverterTokenStoreDefaultTokenServices

  • JwtAccessTokenConverter使用JKS public key
  • JwtTokenStore使用JwtAccessTokenConverter读取令牌。
  • DefaultTokenServices使用JwtTokenStore来保留令牌。

一起测试

为了一起测试,我们需要同时旋转Authorization ServerResource Server ,在我的设置中,它将相应地在端口90009100上运行。

生成令牌

$ curl -u clientId:secret -X POST localhost:9000/oauth/token\?grant_type=password\&username=user\&password=pass{"access_token" : "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDgxODk0NDUsInVzZXJfbmFtZSI6InVzZXIiLCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiYjFjYWQ3MTktZTkwMS00Njk5LTlhOWEtYTIwYzk2NDM5NjAzIiwiY2xpZW50X2lkIjoiY2xpZW50SWQiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXX0.LkQ3KAj2kPY7yKmwXlhIFaHtt-31mJGWPb-_VpC8PWo9IBUpZQxg76WpahBJjet6O1ICx8b5Ab2CxH7ErTl0tL1jk5VZ_kp66E9E7bUQn-C09CY0fqxAan3pzpGrJsUvcR4pzyzLoRCuAqVRF5K2mdDQUZ8NaP0oXeVRuxyRdgjwMAkQGHpFC_Fk-7Hbsq2Y0GikD0UdkaH2Ey_vVyKy5aj3NrAZs62KFvQfSbifxd4uBHzUJSkiFE2Cx3u1xKs3W2q8MladwMwlQmWJROH6lDjQiybUZOEhJaktxQYGAinScnm11-9WOdaqohcr65PAQt48__rMRi0TUgvsxpz6ow","token_type" : "bearer","refresh_token" : "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJ1c2VyIiwic2NvcGUiOlsicmVhZCIsIndyaXRlIl0sImF0aSI6ImIxY2FkNzE5LWU5MDEtNDY5OS05YTlhLWEyMGM5NjQzOTYwMyIsImV4cCI6MTU1MDc4MTE0NSwiYXV0aG9yaXRpZXMiOlsiUk9MRV9VU0VSIl0sImp0aSI6Ijg2OWFjZjM2LTJiODAtNGY5Ni04MzUwLTA5NTgyMzE3NTAzMCIsImNsaWVudF9pZCI6ImNsaWVudElkIn0.TDQwUNb627-f0-Cjn1vWZXFpzZSGpeKZq85ivA9zY_atOXM2WfjOxTLE6phnNLevjLSNAGrx1skm_sx6leQlrrmDi36nwiR7lvhv8xMbn1DkF5KaoWPhldW7GHsSIiauMu_cJ5Kmq89ZOEOlxYoXlLwfWYo75ISkKNYqko98yDogGrRAJxtc1aKIBLypLchhoCf8w43efd11itwvBdaLIb5ACfN30kztUqQtbeL8voQP6tOsRZbCgbOOKMTulOCRyBvaora4GJDV2qdvXdCUT-kORKDj9liqt2ae7OJzb2FuuXCGqBUrxYYK-H-wdwh7XFkXVe74Lev9YDUbyEmDHg","expires_in" : 299,"scope" : "read write","jti" : "b1cad719-e901-4699-9a9a-a20c96439603"
}

访问资源

既然已经生成了令牌,请复制access_token并将其添加到Authorization HTTP Header上的请求中,例如:

curl localhost:9100/me -H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDgxODk0NDUsInVzZXJfbmFtZSI6InVzZXIiLCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiYjFjYWQ3MTktZTkwMS00Njk5LTlhOWEtYTIwYzk2NDM5NjAzIiwiY2xpZW50X2lkIjoiY2xpZW50SWQiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXX0.LkQ3KAj2kPY7yKmwXlhIFaHtt-31mJGWPb-_VpC8PWo9IBUpZQxg76WpahBJjet6O1ICx8b5Ab2CxH7ErTl0tL1jk5VZ_kp66E9E7bUQn-C09CY0fqxAan3pzpGrJsUvcR4pzyzLoRCuAqVRF5K2mdDQUZ8NaP0oXeVRuxyRdgjwMAkQGHpFC_Fk-7Hbsq2Y0GikD0UdkaH2Ey_vVyKy5aj3NrAZs62KFvQfSbifxd4uBHzUJSkiFE2Cx3u1xKs3W2q8MladwMwlQmWJROH6lDjQiybUZOEhJaktxQYGAinScnm11-9WOdaqohcr65PAQt48__rMRi0TUgvsxpz6ow"{"authorities" : [ {"authority" : "ROLE_GUEST"} ],"details" : {"remoteAddress" : "127.0.0.1","sessionId" : null,"tokenValue" : "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDgyMzcxNDEsInVzZXJfbmFtZSI6Imd1ZXN0IiwiYXV0aG9yaXRpZXMiOlsiUk9MRV9HVUVTVCJdLCJqdGkiOiIzNDk1ODE1MC0wOGJkLTQwMDYtYmNhMC1lM2RkYjAxMGU2NjUiLCJjbGllbnRfaWQiOiJjbGllbnRJZCIsInNjb3BlIjpbInJlYWQiLCJ3cml0ZSJdfQ.WUwAh-aKgh_Bqk-a9ijw67EI6H8gFrb3D_WdwlEcITskIybhacHjT6E7cUXjdBT7GCRvvJ-yxzFJIQyI6y0t61SInpqVG2GlAwtTxR5reG0e4ZtcKoq2rbQghK8hWenGplGT31kjDY78zZv-WqCAc0-MM4cC06fTXFzdhsdueY789lCasSD4WMMC6bWbN098lHF96rMpCdlW13EalrPgcKeuvZtUBrC8ntL8Bg3LRMcU1bFKTRAwlVxw1aYyqeEN4NSxkiSgQod2dltA-b3c15L-fXoOWNGnPB68hqgK48ymuemRQTSg3eKmHFAQdDL6pxQ8_D_ZWAL3QhsKQVGDKg","tokenType" : "Bearer","decodedDetails" : null},"authenticated" : true,"userAuthentication" : {"authorities" : [ {"authority" : "ROLE_GUEST"} ],"details" : null,"authenticated" : true,"principal" : "guest","credentials" : "N/A","name" : "guest"},"credentials" : "","principal" : "guest","clientOnly" : false,"oauth2Request" : {"clientId" : "clientId","scope" : [ "read", "write" ],"requestParameters" : {"client_id" : "clientId"},"resourceIds" : [ ],"authorities" : [ ],"approved" : true,"refresh" : false,"redirectUri" : null,"responseTypes" : [ ],"extensions" : { },"grantType" : null,"refreshTokenRequest" : null},"name" : "guest"
}

脚注

  • 本教程使用的代码可以在GitHub上找到
  • OAuth 2.0
  • Spring Security Java配置预览
  • Spring Boot 2 –迁移指南
  • Spring– OAuth2开发人员指南

翻译自: https://www.javacodegeeks.com/2019/03/centralized_-authorization_-oauth2_jwt.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/343590.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

什么是物理隔离?物理隔离光端机是什么?

什么是物理隔离&#xff1f; 物理隔离&#xff0c;是指采用物理方法将内网与外网隔离从而避免入侵或信息泄露的风险的技术手段。物理隔离主要用来解决网络安全问题的&#xff0c;尤其是在那些需要绝对保证安全的保密网&#xff0c;专网和特种网络与互联网进行连接时&#xff0c…

linux mysql make_二、linux-mysql -cmake方式安装mysql 5.5

1.安装解压cmake包cmake软件cd /home/oldboy/tools/tar xf cmake-2.8.8.tar.gzcd cmake-2.8.8./configure#CMake has bootstrapped. Now run gmake.gmakegmake installcd ../2.依赖包yum install ncurses-devel -y3.安装mysql创建用户和组groupadd mysqluseradd mysql -s /sbi…

【渝粤教育】国家开放大学2018年春季 7405-21T面向对象程序设计(本) 参考试题

科目编号&#xff1a;7405 座位号 2018-2019学年度第二学期期末考试 面向对象程序设计&#xff08;本&#xff09; 试题 2018年 7 月 一、单选题&#xff08;本大题共10小题&#xff0c;每小题3分&#xff0c;共计30分&#xff09; &#xff08;★请考生务必将答案填入到下面对…

太极虚拟服务器,太极 中标 云服务器

太极 中标 云服务器 内容精选换一换华为云帮助中心&#xff0c;为用户提供产品简介、价格说明、购买指南、用户指南、API参考、最佳实践、常见问题、视频帮助等技术文档&#xff0c;帮助您快速上手使用华为云服务。云服务器组是对云服务器的一种逻辑划分&#xff0c;云服务器组…

【渝粤教育】国家开放大学2018年春季 8617-21T燃气仪表与自动化 参考试题

科目编号&#xff1a;8617 座位号 2017-2018学年度第二学期期末考试 燃气仪表与自动化 试题 2018年 7月 一、填空题&#xff08;本大题共10空&#xff0c;每空3分&#xff0c;共计30分&#xff09; 1&#xff0e;目前市场上比较著名的软件系统集成平台有 、 、及 。 2&#xf…

什么是自愈环网光端机?

对于光端机这块&#xff0c;相信大家都有所了解。但是&#xff0c;什么是自愈环网光端机呢&#xff1f;想必很多朋友对此不是很了解&#xff0c;相信大家可能会很感兴趣了解下自愈环网光端机吧。接下来就由飞畅科技的小编来为大家详细介绍下什么是自愈环网光端机吧&#xff0c;…

gui界面怎么分页_什么是用户界面和体验设计

本文译自 Mikos Philips 的 UI vs UX  —  A Guide to UI Design因为发现仍然有小伙伴跑来问我比较基础的专业划分问题&#xff0c;所以翻译了这篇科普文。——用户界面&#xff08;UI, User Interface&#xff09;设计是设计软件产品所涉及到的几个交叉学科之一。不论是用户…

【渝粤教育】国家开放大学2018年春季 8638-22T薪酬制度与薪酬管理 参考试题

科目编号&#xff1a;8638 座位号 2017-2018学年度第二学期期末考试 薪酬制度与薪酬管理 试题 2018年 7 月 一、单选题&#xff08;本大题共10小题&#xff0c;每小题3分&#xff0c;共计30分&#xff09; &#xff08;★请考生务必将答案填入到下面对应序号的答题框中★&…

无忧无盘服务器,无忧网维无盘系统新手快速部署.doc

无忧网维无盘系统新手快速部署新手上路之无忧无盘系统快速部署无忧无盘系统支持控制台管理多台无盘服务器&#xff0c;进行远程操作统一管理。与其他的无盘系统不同&#xff0c;第一步需要新添加无盘服务器具体步骤&#xff1a;&#xff11;) 添加无盘服务器??操作步骤1)? ?…

詹金斯搭建_与詹金斯一起连续交付Heroku

詹金斯搭建如果您安装了Jenkins Git插件&#xff0c;那么利用Jenkins并针对Heroku的连续交付管道的设置就非常简单。 通过此管道&#xff0c;对特定Git分支的更改将导致Heroku部署。 为了使此部署过程正常运行&#xff0c;您应该至少使用两个Git分支&#xff0c;因为您希望有一…

【渝粤教育】国家开放大学2018年春季 0025-21T数据结构 参考试题

编号&#xff1a;0025 座位号&#xff1a; 17-18学年第1学期期末考试 数据结构 试题 一、选择题&#xff1a;&#xff08;每题4分&#xff0c;共20分&#xff09; 1&#xff0e;假定一个顺序队列的队首和队尾指针分别为front和rear&#xff0c;存放该队列的数组长度为N&#…

工业交换机品牌选择时注意的事项介绍

杭州飞畅科技有限公司是一家致力于光纤传输设备、数据通信设备及网络接入设备为主的集研发、生产、销售和服务为一体的高新技术企业&#xff0c;公司自成立以来就开始研发生产安防通信产品&#xff0c;采用高稳定性的通信技术制造安防产品&#xff0c;使得安防产品的稳定性极高…

【渝粤教育】国家开放大学2018年春季 0675-21T中级财务会计(2) 参考试题

科目编号&#xff1a;0675 座位号 2017-2018学年度第二学期期末考试 中级财务会计&#xff08;2&#xff09; 试题 2018年 7 月 一、单选题&#xff08;本大题共10小题&#xff0c;每小题3分&#xff0c;共计30分&#xff09; &#xff08;★请考生务必将答案填入到下面对应序…

使用Spring Boot和MongoDB构建一个反应式应用程序

“我喜欢编写身份验证和授权代码。” 〜从来没有Java开发人员。 厌倦了一次又一次地建立相同的登录屏幕&#xff1f; 尝试使用Okta API进行托管身份验证&#xff0c;授权和多因素身份验证。 如果您要处理大量流数据&#xff0c;反应式应用程序可让您更好地扩展。 它们是非阻塞…

c语言 freopen txt_C语言:freopen函数

当我们求解acm题目时&#xff0c;通常在设计好算法和程序后&#xff0c;要在调试环境(例如VC等)中运行程序&#xff0c;输入测试数据&#xff0c;当能得到正确运行结果后&#xff0c;才将程序提交到oj中。但由于调试往往不能一次成功&#xff0c;每次运行时&#xff0c;都要重新…

工业交换机如何判断性能的好坏呢?

当前的智能控制系统和工厂自动化系统常常采用工业交换机完成工业控制任务&#xff0c;工业以太网的应用非常普及。对于工业交换机的选购&#xff0c;把握工业交换机的主要性能指标是关键。那么&#xff0c;工业交换机如何判断性能的好坏呢&#xff1f;接下来就由飞畅科技的小编…

【渝粤教育】国家开放大学2018年春季 建筑结构基础 参考试题

科目编号&#xff1a;8 -6-6- 6 座位号 2017-2018学年度第二学期期末考试 建筑结构基础 试题 2018年 7 月 一、单选题&#xff08;本大题共10小题&#xff0c;每小题4分&#xff0c;共计40分&#xff09; &#xff08;★请考生务必将答案填入到下面对应序号的答题框中★&#…

zune自搭虚拟服务器离线升级,Zune 30g 固件更新至 3.3 记录

换成64位Win7后Zune的桌面管理器木有了&#xff0c;而64位版的Zune 1.3桌面安装麻烦死个人&#xff0c;各大论坛的115下载连接无一例外的都断链了&#xff0c;无奈只好把Zune升级至最新的3.3。升级记录&#xff1a;1. 在Zune官网 http://www.zune.net/zh-CH 下载最新版的桌面管…

21秋期末考试土力学与地基基础10445k1

1、严寒地区的粘土砖强度不会受到地基土中含水量的影响。&#xff08;2.5 分&#xff09; A&#xff0e;错误 B&#xff0e;正确 2、粘性土根据其塑性指数的不同&#xff0c;又可分为粘土和粉质粘土。&#xff08;2.5 分&#xff09; A&#xff0e;错误 B&#xff0e;正确 3、任…

工业交换机芯片选择需要注意什么事项呢?

相信不少工业级交换机品牌&#xff0c;在其工业交换机机内的芯片选择上一定很慎重&#xff0c;因为工业交换价的芯片将影响到其以后的使用&#xff0c;工业交换机常用的芯片有Marvell和Broadcom平台。那么&#xff0c;工业交换机芯片选择要注意什么呢&#xff1f;接下来就由飞畅…