Spring cloud oauth2搭建OAuth2.0授权服务
我理解的OAuth2.0
首先这是一种授权协议,个人理解这个协议最大的好处是可以使用第三方client进行登录,比如我们在登录csdn博客的时候,可以不必使用用户名密码,直接微信或者qq扫码登录即可。相比与普通的用户名密码登录授权,OAuth2.0多了一个client的概念,client即是你开发的网站,比如要接入微信的登录,你需要在微信网站应用开发创建审核相应的材料,然后获取APPId,此APPID即是client id,通过此client id,即可以使用微信的授权服务,获取token,进行网站的访问。
此处完全是我自己对于OAuth2的理解,如果不对之处,欢迎大佬批评指正。
OAuth2.0的四种授权模式
1. 授权码许可类型(Authorization Code)
2.隐式许可类型(Implicit)
3.客户端凭据许可(Client Credentials)
4.资源拥有者凭据许可(Resource Owner Password Credentials)
在这里我只说一下最复杂的授权码许可类型,其他的三种请参考OAuth 2.0 的四种方式。
授权码许可类型是最复杂的授权模式,其流程如下所示。登录客户端需要先获得授权码(authorization_code),然后利用code换取access_token。
代码讲解
总体概述
代码讲解分为两个部分,一个是授权服务,一个是资源服务,资源服务代码主要为了验证生成的access_token能访问相应的资源。
整个项目的pom文件如下。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://maven.apache.org/POM/4.0.0"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.hz.oauth</groupId><artifactId>authority-management-sys-2.0</artifactId><version>1.0</version><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.2.1.RELEASE</version></parent><modules><module>oauth2-resource</module><module>oauth2-server</module></modules><packaging>pom</packaging><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version><org.apache.common.lang3.version>3.12.0</org.apache.common.lang3.version><spring-framework.cloud.version>Greenwich.SR4</spring-framework.cloud.version><org.apache.common.beanutils.version>1.9.4</org.apache.common.beanutils.version><com.baomidou.mybatis-plus.version>3.4.3</com.baomidou.mybatis-plus.version><mysql.connector.java.version>8.0.12</mysql.connector.java.version><com.alibaba.fastjson.version>1.2.76</com.alibaba.fastjson.version><!--Lombok--><lombok.version>1.18.10</lombok.version><commons-io.version>2.6</commons-io.version><javadoc.version>3.0.0</javadoc.version><maven-release-plugin.version>2.5.3</maven-release-plugin.version></properties><dependencyManagement><dependencies><dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-dependencies</artifactId><version>${spring-framework.cloud.version}</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>${lombok.version}</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>${org.apache.common.lang3.version}</version></dependency><dependency><groupId>commons-beanutils</groupId><artifactId>commons-beanutils</artifactId><version>${org.apache.common.beanutils.version}</version></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>${com.baomidou.mybatis-plus.version}</version></dependency><!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>${mysql.connector.java.version}</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>${com.alibaba.fastjson.version}</version></dependency></dependencies></dependencyManagement><build><plugins><!-- for create custom archetype template --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-archetype-plugin</artifactId><version>3.0.1</version></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-release-plugin</artifactId><version>${maven-release-plugin.version}</version></plugin></plugins></build></project>
oauth2-server
最为重要的是两个配置文件类,一个是授权服务的配置,一个是security的安全服务配置,security的安全服务配置重要的是把授权服务相应的地址配置允许匿名访问。
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://maven.apache.org/POM/4.0.0"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><groupId>com.hz.oauth</groupId><artifactId>authority-management-sys-2.0</artifactId><version>1.0</version></parent><artifactId>oauth2-server</artifactId><modelVersion>4.0.0</modelVersion><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.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId></dependency><dependency><groupId>commons-beanutils</groupId><artifactId>commons-beanutils</artifactId></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency></dependencies><build><plugins><!-- for create custom archetype template --><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-archetype-plugin</artifactId><version>3.0.1</version></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-release-plugin</artifactId><version>${maven-release-plugin.version}</version></plugin><plugin><groupId>org.sonarsource.scanner.maven</groupId><artifactId>sonar-maven-plugin</artifactId><version>3.0.2</version></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><configuration><source>1.8</source><target>1.8</target></configuration></plugin></plugins><resources><resource><directory>src/main/java</directory><includes><include>**/*.xml</include></includes></resource><resource><directory>src/main/webapp</directory><targetPath>META-INF/resources</targetPath><includes><include>**/**</include></includes></resource><resource><directory>src/main/resources</directory><includes><include>**/**</include></includes></resource><resource><directory>src/main/resources</directory><filtering>true</filtering><excludes><exclude>**/*.jks</exclude></excludes></resource><resource><directory>src/main/resources</directory><filtering>false</filtering><includes><include>**/*.jks</include></includes></resource></resources></build></project>
SysAuthorizationConfig
在该类配置中,授权码的保存,用户的授权记录保存在数据库中,其生成数据库代码为
DROP TABLE IF EXISTS `oauth_approvals`;
CREATE TABLE `oauth_approvals` (`userId` varchar(256) DEFAULT NULL,`clientId` varchar(256) DEFAULT NULL,`partnerKey` varchar(32) DEFAULT NULL,`scope` varchar(256) DEFAULT NULL,`status` varchar(10) DEFAULT NULL,`expiresAt` datetime DEFAULT NULL,`lastModifiedAt` datetime DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;
DROP TABLE IF EXISTS `oauth_code`;
CREATE TABLE `oauth_code` (`code` varchar(255) DEFAULT NULL,`authentication` blob
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
SysAuthorizationConfig类配置代码
/*** @program: OAuth-2.0* @author: zgr* @create: 2021-07-20 11:42**/
@Configuration
@EnableAuthorizationServer
public class SysAuthorizationConfig extends AuthorizationServerConfigurerAdapter {private final DataSource dataSource;private final SysClientService sysClientService;private final AuthenticationManager authenticationManager;public SysAuthorizationConfig(DataSource dataSource, SysClientService sysClientService, AuthenticationManager authenticationManager) {this.dataSource = dataSource;this.sysClientService = sysClientService;this.authenticationManager = authenticationManager;}/*** 维护一套客户端的信息 即第三方应用软件申请时记录的一些基本信息** @param clients* @throws Exception*/@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {clients.withClientDetails(sysClientService);}/*** 打开验证token的访问权限** @param security*/@Overridepublic void configure(AuthorizationServerSecurityConfigurer security) {security.allowFormAuthenticationForClients().tokenKeyAccess("isAuthenticated()").checkTokenAccess("permitAll()").passwordEncoder(new BCryptPasswordEncoder());}/*** 配置了令牌存储方式为jwt* 配置JWT Token的非对称加密来进行签名* 配置一个自定义的Token增强器,把更多信息放入Token中* 配置使用JDBC数据库方式来保存用户的授权批准记录** @param endpoints*/@Overridepublic void configure(AuthorizationServerEndpointsConfigurer endpoints) {TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();tokenEnhancerChain.setTokenEnhancers(Arrays.asList(tokenEnhancer(), jwtTokenEnhancer()));endpoints.approvalStore(approvalStore()).authorizationCodeServices(authorizationCodeServices()).tokenEnhancer(tokenEnhancerChain).tokenStore(tokenStore()).authenticationManager(authenticationManager);}/*** 使用数据库方式来保存授权码** @return*/@Beanpublic AuthorizationCodeServices authorizationCodeServices() {return new JdbcAuthorizationCodeServices(dataSource);}/*** 采用jwt方式存储token** @return*/@Beanpublic TokenStore tokenStore() {return new JwtTokenStore(jwtTokenEnhancer());}/*** 使用数据库方式存储用户的批准授权记录** @return*/@Beanpublic JdbcApprovalStore approvalStore() {return new JdbcApprovalStore(dataSource);}/*** 自定义token增强器** @return*/@Beanpublic TokenEnhancer tokenEnhancer() {return new SysTokenEnhancer();}/*** jwt 验证** @return*/@Beanprotected JwtAccessTokenConverter jwtTokenEnhancer() {JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();// 设置jwt加解密秘钥,不设置会随机一个jwtAccessTokenConverter.setSigningKey(Constant.JWT_SECRET);return jwtAccessTokenConverter;}
}
SysSecurityConfig
*** @program: authority-management-sys-2.0* @author: zgr* @create: 2021-08-03 09:55**/@Configuration
@EnableWebSecurity
public class SysSecurityConfig extends WebSecurityConfigurerAdapter {private final SysUserService sysUserService;private final SysLogoutHandler sysLogoutHandler;private final SysLogoutSuccessHandler sysLogoutSuccessHandler;public SysSecurityConfig(SysUserService sysUserService, SysLogoutHandler sysLogoutHandler, SysLogoutSuccessHandler sysLogoutSuccessHandler) {this.sysUserService = sysUserService;this.sysLogoutHandler = sysLogoutHandler;this.sysLogoutSuccessHandler = sysLogoutSuccessHandler;}/*** 配置认证管理器** @return* @throws Exception*/@Bean@Overridepublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}/*** 这里是对认证管理器的添加配置,自定义用户详情** @param auth* @throws Exception*/@Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(sysUserService).passwordEncoder(new BCryptPasswordEncoder());}//web ignore比较适合配置前端相关的静态资源,它是完全绕过spring security的所有filter的@Overridepublic void configure(WebSecurity web) throws Exception {web.ignoring().antMatchers("/resource");}/*** 安全请求配置,这里配置的是security的部分,这里配置全部通过,安全拦截在资源服务的配置文件中配置,* 要不然访问未验证的接口将重定向到登录页面,前后端分离的情况下这样并不友好,无权访问接口返回相关错误信息即可** @param http* @return void*/@Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin().loginPage("/login").and().logout().addLogoutHandler(sysLogoutHandler).logoutSuccessHandler(sysLogoutSuccessHandler).and()// 由于使用的是JWT,我们这里不需要csrf.csrf().disable().cors().and().authorizeRequests().antMatchers("/login", "/oauth/authorize").permitAll()// 除上面外的所有请求全部需要鉴权认证.anyRequest().authenticated();}
}
客户端服务ClientDetailsService
在SysAuthorizationConfig中,我们配置的是客户端 服务为自定义的sysClientService。实际应用中,通过client id查出对应客户端的信息,查询其拥有的resource资源权限,生成access_token。
/*** 维护一套客户端的信息 即第三方应用软件申请时记录的一些基本信息** @param clients* @throws Exception*/@Overridepublic void configure(ClientDetailsServiceConfigurer clients) throws Exception {clients.withClientDetails(sysClientService);}
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class SysClientDetails implements ClientDetails {private String clientId;private Set<String> resourceId;private Boolean isSecretRequired;private String clientSecret;private Boolean isScoped;private Set<String> scope;private Set<String> authorizedGrantTypes;private Set<String> registeredRedirectUri;private Integer accessTokenValiditySeconds;private Integer refreshTokenValiditySeconds;private Boolean isAutoApprove;private Map<String, Object> additionalInformation;@Overridepublic String getClientId() {return this.clientId;}@Overridepublic Set<String> getResourceIds() {return this.resourceId;}@Overridepublic boolean isSecretRequired() {return this.isSecretRequired;}@Overridepublic String getClientSecret() {return this.clientSecret;}@Overridepublic boolean isScoped() {return this.isScoped;}@Overridepublic Set<String> getScope() {return this.scope;}@Overridepublic Set<String> getAuthorizedGrantTypes() {return this.authorizedGrantTypes;}@Overridepublic Set<String> getRegisteredRedirectUri() {return this.registeredRedirectUri;}@Overridepublic Collection<GrantedAuthority> getAuthorities() {return Collections.emptyList();}@Overridepublic Integer getAccessTokenValiditySeconds() {return this.accessTokenValiditySeconds;}@Overridepublic Integer getRefreshTokenValiditySeconds() {return this.refreshTokenValiditySeconds;}@Overridepublic boolean isAutoApprove(String scope) {return this.isAutoApprove;}@Overridepublic Map<String, Object> getAdditionalInformation() {return this.additionalInformation;}
}
/*** @program: authority-management-sys-2.0* @author: zgr* @create: 2021-08-03 10:12**/@Service
@Slf4j
public class SysClientServiceImpl implements SysClientService {@Overridepublic ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {log.info("查找客户端id为 {} 的客户端详情", clientId);//实际使用中可以从数据库查询客户端详细信息,本例子中直接构造一个客户端Set<String> resourceIds = new HashSet<>();resourceIds.add("userservice1");Set<String> scopes = new HashSet<>(1);scopes.add("sever");Set<String> grantTypes = new HashSet<>(5);grantTypes.add("password");grantTypes.add("authorization_code");Set<String> registeredRedirectUris = new HashSet<>(1);registeredRedirectUris.add("https://baidu.com");return SysClientDetails.builder()//客户端id.clientId("userservice1")//拥有的资源id.resourceId(resourceIds)//是否需要secret.isSecretRequired(true)//secret加密方式.clientSecret(new BCryptPasswordEncoder().encode("1234"))//权限范围.scope(scopes)//支持的授权模式,四种模式支持哪几种.authorizedGrantTypes(grantTypes)//官方注册的回调地址,这个地址是需要和授权请求的回调地址一致.registeredRedirectUri(registeredRedirectUris).isScoped(true)//access_token的有效时间.accessTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(2))//refresh_token的有效时间.refreshTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(30))//是否可以自主授权.isAutoApprove(true).build();}
}
postman演示结果
1.资源有着凭据许可
完整的返回数据:
{"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsidXNlcnNlcnZpY2UxIl0sInVzZXJfbmFtZSI6ImFkbWluIiwic2NvcGUiOlsic2V2ZXIiXSwiZXhwIjoxNjI4NzU3NTMzLCJ1c2VyRGV0YWlscyI6eyJpZCI6MSwidXNlcm5hbWUiOiJhZG1pbiIsInBhc3N3b3JkIjpudWxsLCJyb2xlcyI6W3siaWQiOjEsIm5hbWUiOiJSRUFEIn0seyJpZCI6MSwibmFtZSI6IldSSVRFIn1dLCJhdXRob3JpdGllcyI6W3siYXV0aG9yaXR5IjoiUkVBRCJ9LHsiYXV0aG9yaXR5IjoiV1JJVEUifV0sImVuYWJsZWQiOnRydWUsImNyZWRlbnRpYWxzTm9uRXhwaXJlZCI6dHJ1ZSwiYWNjb3VudE5vbkV4cGlyZWQiOnRydWUsImFjY291bnROb25Mb2NrZWQiOnRydWV9LCJhdXRob3JpdGllcyI6WyJSRUFEIiwiV1JJVEUiXSwianRpIjoiODAwZjgwMmUtZTUzNS00N2RiLTkzOTUtZjY5ZmQxM2Q3ZjNjIiwiY2xpZW50X2lkIjoidXNlcnNlcnZpY2UxIn0.N8HLGnDbRiN560SuE_a4IlqEZIk3yCpzpyEXH3VDb1w","token_type": "bearer","refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsidXNlcnNlcnZpY2UxIl0sInVzZXJfbmFtZSI6ImFkbWluIiwic2NvcGUiOlsic2V2ZXIiXSwiYXRpIjoiODAwZjgwMmUtZTUzNS00N2RiLTkzOTUtZjY5ZmQxM2Q3ZjNjIiwiZXhwIjoxNjMxMTc2NzMyLCJ1c2VyRGV0YWlscyI6eyJpZCI6MSwidXNlcm5hbWUiOiJhZG1pbiIsInBhc3N3b3JkIjpudWxsLCJyb2xlcyI6W3siaWQiOjEsIm5hbWUiOiJSRUFEIn0seyJpZCI6MSwibmFtZSI6IldSSVRFIn1dLCJhdXRob3JpdGllcyI6W3siYXV0aG9yaXR5IjoiUkVBRCJ9LHsiYXV0aG9yaXR5IjoiV1JJVEUifV0sImVuYWJsZWQiOnRydWUsImNyZWRlbnRpYWxzTm9uRXhwaXJlZCI6dHJ1ZSwiYWNjb3VudE5vbkV4cGlyZWQiOnRydWUsImFjY291bnROb25Mb2NrZWQiOnRydWV9LCJhdXRob3JpdGllcyI6WyJSRUFEIiwiV1JJVEUiXSwianRpIjoiZDMyNGQ4ZjEtMjBiMy00MTg2LTk4NzMtZjg2NDhkNjI1ZTc5IiwiY2xpZW50X2lkIjoidXNlcnNlcnZpY2UxIn0.KbqODRoK0hjrrHWeKvtZ2HNkcREPFmuv7yvOrZGzEnE","expires_in": 172799,"scope": "sever","userDetails": {"id": 1,"username": "admin","password": null,"roles": [{"id": 1,"name": "READ"},{"id": 1,"name": "WRITE"}],"authorities": [{"authority": "READ"},{"authority": "WRITE"}],"enabled": true,"credentialsNonExpired": true,"accountNonExpired": true,"accountNonLocked": true},"jti": "800f802e-e535-47db-9395-f69fd13d7f3c"
}
2.授权码模式
在浏览器输入地址http://localhost:8086/api/v1/oauth/authorize?response_type=code&client_id=userservice1&redirect_uri=https://baidu.com,会跳转到授权页面,授权页面利用spring mvc定义了一个页面放在resources目录下。
<!DOCTYPE html>
<html class="uk-height-1-1" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"/><title>OAuth2 Demo</title><link href="https://cdnjs.cloudflare.com/ajax/libs/uikit/2.26.3/css/uikit.gradient.min.css" rel="stylesheet"/>
</head><body class="uk-height-1-1"><div class="uk-vertical-align uk-text-center uk-height-1-1"><div class="uk-vertical-align-middle" style="width: 250px;"><h1>Login Form</h1><p class="uk-text-danger" th:if="${param.error}">用户名或密码错误...</p><form class="uk-panel uk-panel-box uk-form" method="post" th:action="@{/login}"><div class="uk-form-row"><input class="uk-width-1-1 uk-form-large" name="username" placeholder="Username" type="text"value="admin"/></div><div class="uk-form-row"><input class="uk-width-1-1 uk-form-large" name="password" placeholder="Password" type="password"value="admin"/></div><div class="uk-form-row"><button class="uk-width-1-1 uk-button uk-button-primary uk-button-large">Login</button></div></form></div>
</div>
</body>
</html>
跳转到登录页面,点击登录,登录成功后,跳转到百度页面,并得到code授权码
拿到获取的code,生成access_token
完整的返回信息
{"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsidXNlcnNlcnZpY2UxIl0sInVzZXJfbmFtZSI6ImFkbWluIiwic2NvcGUiOlsic2V2ZXIiXSwiZXhwIjoxNjI4NzU4Mjc2LCJ1c2VyRGV0YWlscyI6eyJpZCI6MSwidXNlcm5hbWUiOiJhZG1pbiIsInBhc3N3b3JkIjpudWxsLCJyb2xlcyI6W3siaWQiOjEsIm5hbWUiOiJSRUFEIn0seyJpZCI6MSwibmFtZSI6IldSSVRFIn1dLCJhdXRob3JpdGllcyI6W3siYXV0aG9yaXR5IjoiUkVBRCJ9LHsiYXV0aG9yaXR5IjoiV1JJVEUifV0sImVuYWJsZWQiOnRydWUsImNyZWRlbnRpYWxzTm9uRXhwaXJlZCI6dHJ1ZSwiYWNjb3VudE5vbkV4cGlyZWQiOnRydWUsImFjY291bnROb25Mb2NrZWQiOnRydWV9LCJhdXRob3JpdGllcyI6WyJSRUFEIiwiV1JJVEUiXSwianRpIjoiZGM5Y2FjN2UtN2M3My00YThkLWEzMzYtZTI2ZmNjYzllODE0IiwiY2xpZW50X2lkIjoidXNlcnNlcnZpY2UxIn0.sOhH-eFcldWnnOfZmDqyuPkl4Qkg8LyJnPZ0gwubBWc","token_type": "bearer","refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsidXNlcnNlcnZpY2UxIl0sInVzZXJfbmFtZSI6ImFkbWluIiwic2NvcGUiOlsic2V2ZXIiXSwiYXRpIjoiZGM5Y2FjN2UtN2M3My00YThkLWEzMzYtZTI2ZmNjYzllODE0IiwiZXhwIjoxNjMxMTc3NDc2LCJ1c2VyRGV0YWlscyI6eyJpZCI6MSwidXNlcm5hbWUiOiJhZG1pbiIsInBhc3N3b3JkIjpudWxsLCJyb2xlcyI6W3siaWQiOjEsIm5hbWUiOiJSRUFEIn0seyJpZCI6MSwibmFtZSI6IldSSVRFIn1dLCJhdXRob3JpdGllcyI6W3siYXV0aG9yaXR5IjoiUkVBRCJ9LHsiYXV0aG9yaXR5IjoiV1JJVEUifV0sImVuYWJsZWQiOnRydWUsImNyZWRlbnRpYWxzTm9uRXhwaXJlZCI6dHJ1ZSwiYWNjb3VudE5vbkV4cGlyZWQiOnRydWUsImFjY291bnROb25Mb2NrZWQiOnRydWV9LCJhdXRob3JpdGllcyI6WyJSRUFEIiwiV1JJVEUiXSwianRpIjoiNDdmYjUzMDctYzcyYy00ZjI1LWI4MjEtNTY2MGNlYWYyZGMwIiwiY2xpZW50X2lkIjoidXNlcnNlcnZpY2UxIn0.vmtnmLWsaGBxFxAZ-0nsETBUsZ3muSFKqjffAweRZTQ","expires_in": 172799,"scope": "sever","userDetails": {"id": 1,"username": "admin","password": null,"roles": [{"id": 1,"name": "READ"},{"id": 1,"name": "WRITE"}],"authorities": [{"authority": "READ"},{"authority": "WRITE"}],"enabled": true,"credentialsNonExpired": true,"accountNonExpired": true,"accountNonLocked": true},"jti": "dc9cac7e-7c73-4a8d-a336-e26fccc9e814"
}
oauth2-resource
oauth2-resource为资源,用户在获得access_token后,携带access_token访问资源。最重要的是资源配置类。
@Configuration
//启用资源服务器
@EnableResourceServer
//启用方法注解方式来进行权限控制
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {/*** 声明了资源服务器的ID是userservice,声明了资源服务器的TokenStore是JWT* 注意,这里硬编码了资源id,这个资源id和client所拥有的的资源id需要匹配** @param resources* @throws Exception*/@Overridepublic void configure(ResourceServerSecurityConfigurer resources) {resources.resourceId("userservice1").tokenStore(tokenStore());}/*** 配置TokenStore** @return*/@Beanpublic TokenStore tokenStore() {return new JwtTokenStore(jwtAccessTokenConverter());}/*** 配置解析** @return*/@Beanprotected JwtAccessTokenConverter jwtAccessTokenConverter() {JwtAccessTokenConverter converter = new JwtAccessTokenConverter();//和授权服务的key相同converter.setSigningKey(Constant.JWT_SECRET);return converter;}
}
security配置接口访问权限
@Configuration
@EnableWebSecurity
public class ResourceSecurityConfig extends WebSecurityConfigurerAdapter {//web ignore比较适合配置前端相关的静态资源,它是完全绕过spring security的所有filter的@Overridepublic void configure(WebSecurity web) throws Exception {web.ignoring().antMatchers("/resource");}/*** 安全请求配置,这里配置的是security的部分,这里配置全部通过,安全拦截在资源服务的配置文件中配置,* 要不然访问未验证的接口将重定向到登录页面,前后端分离的情况下这样并不友好,无权访问接口返回相关错误信息即可** @param http* @return void*/@Overrideprotected void configure(HttpSecurity http) throws Exception {http.authorizeRequests().antMatchers("/user/**").authenticated().anyRequest().permitAll();}
}
ctrl接口测试类
*** @author honorzhang*/@RestController
@RequestMapping("/user")
public class ResourceUserController {@Autowiredprivate TokenStore tokenStore;/**** 读权限或写权限可访问,返回登录用户名* @param authentication* @return*/@PreAuthorize("hasAuthority('READ') or hasAuthority('WRITE')")@GetMapping("/name")public BaseResponse<String> name(OAuth2Authentication authentication) {return BaseResponse.success(authentication.getName());}/*** 读权限或写权限可访问,返回登录用户信息** @param authentication* @return*/@PreAuthorize("hasAuthority('READ') or hasAuthority('WRITE')")@GetMappingpublic BaseResponse<OAuth2Authentication> read(OAuth2Authentication authentication) {return BaseResponse.success(authentication);}/*** 只有写权限可以访问,返回访问令牌中的额外信息** @param authentication* @return*/@PreAuthorize("hasAuthority('WRITE') and hasAuthority('ADMIN')")@PostMappingpublic BaseResponse<Object> write(OAuth2Authentication authentication) {OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails();OAuth2AccessToken accessToken = tokenStore.readAccessToken(details.getTokenValue());return BaseResponse.success(accessToken.getAdditionalInformation().getOrDefault("userDetails", null));}
}
postman结果测试
access_token 权限包含WRITE 和 READ权限,没有ADMIN权限。
访问read接口,权限通过。
访问write接口,没有ADMIN权限,不能访问
引用
极客时间OAuth2.0课程
写在最后
本文是根据极客时间的课程,利用spring cloud oauth2 实现的OAuth2.0权限控制。有很多是基于自己的理解,如有不正确之处,请互相指教。
代码连接
https://github.com/airhonor/authority-management-sys-2.0
Spring cloud oauth2搭建OAuth2.0授权服务相关推荐
- 实战干货!Spring Cloud Gateway 整合 OAuth2.0 实现分布式统一认证授权!
今天这篇文章介绍一下Spring Cloud Gateway整合OAuth2.0实现认证授权,涉及到的知识点有点多,有不清楚的可以看下陈某的往期文章. 文章目录如下: 微服务认证方案 微服务认证方案目 ...
- Spring Cloud Gateway 结合 OAuth2 提供 UAA 服务,来袭。
微服务做用户认证和授权一直都是一个难点,随着 OAuth2.0 的密码模式被作废,更是难上加难了.今天胖哥群里的一个群友搭建用户认证授权体系的时候遇到了一些棘手的问题,这让胖哥觉得是时候分享一些思路出 ...
- Spring Cloud Gateway 结合OAuth2提供UAA服务
微服务做用户认证和授权一直都是一个难点,随着OAuth2.0的密码模式被作废,更是难上加难了.今天胖哥群里的一个群友搭建用户认证授权体系的时候遇到了一些棘手的问题,这让胖哥觉得是时候分享一些思路出来了 ...
- 【Spring Cloud 基础设施搭建系列】Spring Cloud Demo项目 将微服务运行在Docker上
文章目录 将微服务运行在Docker上 使用Maven插件构建Docker镜像 使用Maven插件读取Dockerfile进行构建 将插件绑定在某个phase执行 参考 源代码 将微服务运行在Dock ...
- spring cloud java b2b2c o2o分布式 微服务电子商务平台
大型企业分布式互联网电子商务平台,推出PC+微信+APP+云服务的云商平台系统,其中包括B2B.B2C.C2C.O2O.新零售.直播电商等子平台. 需要JAVA Spring Cloud大型企业分布式 ...
- 基于 IdentityServer3 实现 OAuth 2.0 授权服务【密码模式(Resource Owner Password Credentials)】...
密码模式(Resource Owner Password Credentials Grant)中,用户向客户端提供自己的用户名和密码.客户端使用这些信息,向"服务商提供商"索要授权 ...
- Spring Cloud Alibaba搭建(二):Nacos注册中心
官方文档地址:https://nacos.io/zh-cn/docs/quick-start-spring-cloud.html 一.核心功能: 服务注册:Nacos Client会通过发送REST请 ...
- Dubbo将积极适配Spring Cloud生态,Spring Cloud体系或将成为微服务的不二选择!
2016年,我在博客中发表过一篇<微服务架构的基础框架选择:Spring Cloud还是Dubbo?>(http://blog.didispace.com/microservice-fra ...
- Spring Cloud Eureka 入门 (三)服务消费者详解
2019独角兽企业重金招聘Python工程师标准>>> 摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢! "真正的进步 ...
- 疯狂Spring Cloud连载(29)微服务跟踪概述
本文节选自<疯狂Spring Cloud微服务架构实战> 京东购买地址::https://item.jd.com/12256011.html 当当网购买地址::http://product ...
最新文章
- Vue添加新的响应式属性
- 他是第一批推动比特币普及的人 如今早已放弃了信仰
- oracle普通用户使用dbms函数,oracle使用DBMS_SCHEDULER调度作业
- QA测试工程师思维导图
- Linux字符界面操作进阶
- html偷拍代码,一段植入木马的html代码
- 点击按钮,图片和按钮的文字发生改变
- Linux pause函数 详解
- SPOJ QTREE6 lct
- 狗窝里的小日子 ...
- 神话系列之一 C# 开发的操作系统和数据库
- 模型有锯齿_小雕课堂 | 最好的抗锯齿,画质保留提升帧数
- 芒果 mysql插件,NoSQL代表:MongoDB(芒果数据库)
- 数据结构——线段树学习笔记
- 如何计算置信区间,RMSE均方根误差/标准误差:误差平方和的平均数开方
- 微软网盘SkyDrive简单一步获取mp3外链的方法
- HTML+CSS打造简单的横向时间轴
- [aria2c]使用aria2c下载“任务出错”的bt种子
- 数据结构 在顺序表中头插及尾插的实现
- 现在玩cf的计算机配置要求,穿越火线电脑配置要求-玩CF所需的电脑配置
热门文章
- Gululu互动水杯进驻英国皇家玩具品牌Hamleys 践行全球布局战略
- 虚拟机设置共享文件夹之后看不见文件(失败合集+成功分享)
- 【Mysql密码管理】-【管理员密码已知、未知(忘记密码)、破解】
- 计算机分组Excel,【Excel神技能】如何在Excel表格中进行“数据分组”?
- kdj买卖指标公式源码_精品 玩转KDJ【精准买卖提示、源码、副图、说明】
- 国外调查问卷怎么做?
- 【常用表】三角函数基本公式
- Fundamentals of Computer Graphics(4th Ed)--Introduction(计算机图形学翻译级笔记)
- 在WordPress中嵌入YouTube视频的六种不同方式
- linux vi编译显示行号,Linux系统vi或者vim编辑器中如何显示行号