OAuth 2
发明之初是为了解决登录认证过程中的安全性问题,使用“委托”的形式使第三方应用获得数据权限及功能。OAuth 2.0
协议中,使用访问令牌ACCESS_TOKEN代替传统的账号密码,提高了互联网环境下的安全性。
OAuth 2
共分为四种角色:
授权服务:功能开放平台
资源所有者:用户
受保护资源:接口提供方
客户端:第三方软件即接口调用方
实则授权服务和受保护资源可以部署在同一服务器上,也可以部署在不同服务上,因为两种角色是属于同一开发团队。
在微服务环境下使用Spring OAuth 2
实现授权服务流程,需要分成三个模块:
server端:授权服务端,配置OAuth 2
授权服务器信息,负责生成授权码及访问令牌等
resource端:接口提供方,负责解析授权令牌、鉴权、数据提供
client端:第三方应用,负责调用第三方数据
准备工作
一、数据库脚本
SET NAMES utf8mb4;SET FOREIGN_KEY_CHECKS = 0;-- ------------------------------ Table structure for authorities-- ----------------------------DROP TABLE IF EXISTS `authorities`;CREATE TABLE `authorities` ( `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, `authority` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, UNIQUE INDEX `ix_auth_username`(`username`, `authority`) USING BTREE, CONSTRAINT `fk_authorities_users` FOREIGN KEY (`username`) REFERENCES `users` (`username`) ON DELETE RESTRICT ON UPDATE RESTRICT) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;-- ------------------------------ Records of authorities-- ----------------------------INSERT INTO `authorities` VALUES ('reader', 'READ');INSERT INTO `authorities` VALUES ('writer', 'READ,WRITE');-- ------------------------------ Table structure for oauth_approvals-- ----------------------------DROP TABLE IF EXISTS `oauth_approvals`;CREATE TABLE `oauth_approvals` ( `userId` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `clientId` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `partnerKey` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `scope` varchar(256) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `status` varchar(10) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `expiresAt` datetime(0) NULL DEFAULT NULL, `lastModifiedAt` datetime(0) NULL DEFAULT NULL) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;-- ------------------------------ Records of oauth_approvals-- ----------------------------INSERT INTO `oauth_approvals` VALUES ('reader', 'userservice3', NULL, 'FOO', 'APPROVED', '2022-09-30 06:53:34', '2022-08-31 06:53:34');INSERT INTO `oauth_approvals` VALUES ('writer', 'userservice3', NULL, 'FOO', 'APPROVED', '2022-09-30 13:56:15', '2022-08-31 13:56:15');-- ------------------------------ Table structure for oauth_client_details-- ----------------------------DROP TABLE IF EXISTS `oauth_client_details`;CREATE TABLE `oauth_client_details` ( `client_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, `resource_ids` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, `client_secret` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, `scope` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, `authorized_grant_types` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, `web_server_redirect_uri` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, `authorities` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, `access_token_validity` int(0) NULL DEFAULT NULL, `refresh_token_validity` int(0) NULL DEFAULT NULL, `additional_information` varchar(4096) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, `autoapprove` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, PRIMARY KEY (`client_id`) USING BTREE) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;-- ------------------------------ Records of oauth_client_details-- ----------------------------INSERT INTO `oauth_client_details` VALUES ('userservice1', 'userservice', '1234', 'FOO', 'password,refresh_token', '', 'READ,WRITE', 7200, NULL, NULL, 'true');INSERT INTO `oauth_client_details` VALUES ('userservice2', 'userservice', '1234', 'FOO', 'client_credentials,refresh_token', '', 'READ,WRITE', 7200, NULL, NULL, 'true');INSERT INTO `oauth_client_details` VALUES ('userservice3', 'userservice', '1234', 'FOO', 'authorization_code,refresh_token', 'https://baidu.com,https://tev-competition-admin.qstcloud.net,http://localhost:5083/ui/login,http://localhost:8083/ui/login,http://localhost:5082/ui/remoteCall', 'READ,WRITE', 7200, NULL, NULL, 'false');-- ------------------------------ Table structure for oauth_code-- ----------------------------DROP TABLE IF EXISTS `oauth_code`;CREATE TABLE `oauth_code` ( `code` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, `authentication` blob NULL) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;-- ------------------------------ Records of oauth_code-- ------------------------------ ------------------------------ Table structure for users-- ----------------------------DROP TABLE IF EXISTS `users`;CREATE TABLE `users` ( `username` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, `password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, `enabled` tinyint(1) NOT NULL, PRIMARY KEY (`username`) USING BTREE) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;-- ------------------------------ Records of users-- ----------------------------INSERT INTO `users` VALUES ('reader', '$2a$04$C6pPJvC1v6.enW6ZZxX.luTdpSI/1gcgTVN7LhvQV6l/AfmzNU/3i', 1);INSERT INTO `users` VALUES ('writer', '$2a$04$M9t2oVs3/VIreBMocOujqOaB/oziWL0SnlWdt8hV4YnlhQrORA0fS', 1);SET FOREIGN_KEY_CHECKS = 1;
二、项目框架
搭建项目父依赖,并创建三个模块:
cloud-oauth2-client
cloud-oauth2-server
cloud-oauth2-userservice
授权服务器
引入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency>
服务端配置
授权服务配置需要继承自org.springframework.security.oauth2.config.annotation.web.configuration
包下的AuthorizationServerConfigurerAdapter
类,主要配置了用户信息来源、访问权限配置、Token配置。
@Configuration//开启授权服务器@EnableAuthorizationServer public class OAuth2ServerConfiguration extends AuthorizationServerConfigurerAdapter { @Autowired private DataSource dataSource; @Autowired private AuthenticationManager authenticationManager; /** * 我们配置了使用数据库来维护客户端信息。虽然在各种Demo中我们经常看到的是在内存中维护客户端信息,通过配置直接写死在这里。 * 但是,对于实际的应用我们一般都会用数据库来维护这个信息,甚至还会建立一套工作流来允许客户端自己申请ClientID,实现OAuth客户端接入的审批。 * @param clients * @throws Exception */ @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.jdbc(dataSource); } /** * 这里干了两件事儿。首先,打开了验证Token的访问权限(以便之后我们演示)。 * 然后,允许ClientSecret明文方式保存,并且可以通过表单提交(而不仅仅是Basic Auth方式提交),之后会演示到这个。 * @param security * @throws Exception */ @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security.checkTokenAccess("permitAll()") .allowFormAuthenticationForClients().passwordEncoder(NoOpPasswordEncoder.getInstance()); } /** * 干了以下4件事儿: * 1. 配置我们的令牌存放方式为JWT方式,而不是内存、数据库或Redis方式。 * JWT是Json Web Token的缩写,也就是使用JSON数据格式包装的令牌,由.号把整个JWT分隔为头、数据体、签名三部分。 * JWT保存Token虽然易于使用但是不是那么安全,一般用于内部,且需要走HTTPS并配置比较短的失效时间。 * 2. 配置JWT Token的非对称加密来进行签名 * 3. 配置一个自定义的Token增强器,把更多信息放入Token中 * 4. 配置使用JDBC数据库方式来保存用户的授权批准记录 * @param endpoints * @throws Exception */ @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) { TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain(); tokenEnhancerChain.setTokenEnhancers( Arrays.asList(tokenEnhancer(), jwtTokenEnhancer())); endpoints.approvalStore(approvalStore()) .authorizationCodeServices(authorizationCodeServices()) .tokenStore(tokenStore()) .tokenEnhancer(tokenEnhancerChain) .authenticationManager(authenticationManager); } /** * 使用JDBC数据库方式来保存授权码 * @return */ @Bean public AuthorizationCodeServices authorizationCodeServices() { return new JdbcAuthorizationCodeServices(dataSource); } /** * 使用JWT存储 * @return */ @Bean public TokenStore tokenStore() { return new JwtTokenStore(jwtTokenEnhancer()); } /** * 使用JDBC数据库方式来保存用户的授权批准记录 * @return */ @Bean public JdbcApprovalStore approvalStore() { return new JdbcApprovalStore(dataSource); } /** * 自定义的Token增强器,把更多信息放入Token中 * @return */ @Bean public TokenEnhancer tokenEnhancer() { return new CustomTokenEnhancer(); } /** * 配置JWT使用非对称加密方式来验证 * @return */ @Bean protected JwtAccessTokenConverter jwtTokenEnhancer() { KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(new ClassPathResource("jwt.jks"), "mySecretKey".toCharArray()); JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); converter.setKeyPair(keyStoreKeyFactory.getKeyPair("jwt")); return converter; } /** * 配置登录页面的视图信息(其实可以独立一个配置类,这样会更规范) */ @Configuration static class MvcConfig implements WebMvcConfigurer { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("login").setViewName("login"); } }}
自定义Token增强器
主要用于将自定义的更多用户信息放入Token中。
public class CustomTokenEnhancer implements TokenEnhancer { @Override public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) { Authentication userAuthentication = authentication.getUserAuthentication(); if (userAuthentication != null) { Object principal = authentication.getUserAuthentication().getPrincipal(); //把用户标识嵌入JWT Token中去(Key是userDetails) Map<String, Object> additionalInfo = new HashMap<>(); additionalInfo.put("userDetails", principal); ((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo); } return accessToken; }}
安全配置
@Configurationpublic class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private DataSource dataSource; @Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } /** * 配置用户账户的认证方式。显然,我们把用户存在了数据库中希望配置JDBC的方式。 * 此外,我们还配置了使用BCryptPasswordEncoder哈希来保存用户的密码(生产环境中,用户密码肯定不能是明文保存的) * @param auth * @throws Exception */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.jdbcAuthentication() .dataSource(dataSource) .passwordEncoder(new BCryptPasswordEncoder()); } /** * 开放/login和/oauth/authorize两个路径的匿名访问。前者用于登录,后者用于换授权码,这两个端点访问的时机都在登录之前。 * 设置/login使用表单验证进行登录。 * @param http * @throws Exception */ @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/login", "/oauth/authorize") .permitAll() .anyRequest().authenticated() .and() .formLogin().loginPage("/login"); }}
登录页html放在templates目录下:
<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" type="text" placeholder="Username" name="username" value="reader"/> </div> <div class="uk-form-row"> <input class="uk-width-1-1 uk-form-large" type="password" placeholder="Password" name="password" value="reader"/> </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>
受保护资源服务器
引入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-oauth2</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency>
受保护资源配置
创建核心资源服务器配置类。
硬编码了资源服务器的 ID 为
userservice
;现在我们使用的是不落数据库的
JWT
方式 + 非对称加密,需要通过本地公钥进行验证,因此在这里我们配置了公钥的路径。
公钥和密钥有多种配置方式,可以通过读取文件、读取配置文件等方式,只需要保证公钥和私钥配对即可,在本demo中可以直接复用
public.cert
、
@Configuration @EnableResourceServer //启用资源服务器@EnableGlobalMethodSecurity(prePostEnabled = true) //启用方法注解方式来进行权限控制public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter { /** * 声明了资源服务器的ID是userservice,声明了资源服务器的TokenStore是JWT * @param resources * @throws Exception */ @Override public void configure(ResourceServerSecurityConfigurer resources) { resources.resourceId("userservice").tokenStore(tokenStore()); } /** * 配置TokenStore * * @return */ @Bean public TokenStore tokenStore() { return new JwtTokenStore(jwtAccessTokenConverter()); } /** * 配置公钥 * @return */ @Bean protected JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); Resource resource = new ClassPathResource("public.cert"); String publicKey = null; try { publicKey = new String(FileCopyUtils.copyToByteArray(resource.getInputStream())); } catch (IOException e) { e.printStackTrace(); } converter.setVerifierKey(publicKey); return converter; } /** * 配置了除了/user路径之外的请求可以匿名访问 * @param http * @throws Exception */ @Override public void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/user/**").authenticated() .anyRequest().permitAll(); }}
启动项目Server、Resource。
测试
各大开放平台都是推荐使用授权码许可流程,无论是网页版的 Web 应用程序,还是移动应用程序。本次仅演示授权码模式登录。
第一步,打开浏览器访问地址:
http://localhost:8080/oauth/authorize?response_type=code&client_id=userservice3&redirect_uri=https://baidu.com
注意,客户端跳转地址需要和数据库中配置的一致(百度的 URL https://baidu.com我们之前已经在数据库中有配置了)。访问后页面会直接跳转到登录界面,我们使用用户名“reader”、密码“reader”来登录,点击批准,可以发现页面重定向到百度页,并且地址栏最后出现了授权码。
第二步,使用授权码获取访问令牌ACCESS_TOKEN
http://localhost:8080/oauth/token?grant_type=authorization_code&client_id=userservice3&client_secret=1234&code=XKkHGY&redirect_uri=https://baidu.com
redirect_uri后的参数需要在数据库
oauth_client_details
表中配置,不同数值间用英文逗号分隔
Gitee地址: https://gitee.com/FirstMrRight/oauth2.git
从 OAuth2 服务器获取授权授权
搭建好了基于 OWIN 的 OAuth2 服务器之后, 接下来就是如何从服务器取得授权了, 下面就介绍如何实现 OAuth2 定义的四种授权方式。
授权码授权 (Authorization Code Grant)
授权码授权针对机密的客户端优化, 可以同时获取访问凭据 (access token) 和刷新凭据 (refresh token) , 因为是基于 HTTP 重定向的方式, 所以客户端必须能够操纵资源所有者的用户代理(通常是浏览器)并且能够接收从授权服务器重定向过来的请求。
在实现上使用开源的 DotNetOpenAuth 来简化实现代码, DotNetOpenAuth 可以通过 NuGet 获取, 示例代码如下:
// init a new oauth web server client;var authServer = new AuthorizationServerDescription { AuthorizationEndpoint = new Uri(Paths.AuthorizePath), TokenEndpoint = new Uri(Paths.TokenPath)};var webServerClient = new WebServerClient(authServer, clientId, clientSecret);// redirect user user-agent to authorization endpoint;var userAuthorization = webServerClient.PrepareRequestUserAuthorization(new[] { "bio", "notes" });userAuthorization.Send(HttpContext);Response.End();// get access token from request (redirect from oauth server)var authorizationState = webServerClient.ProcessUserAuthorization(Request);if (authorizationState != null) { ViewBag.AccessToken = authorizationState.AccessToken; ViewBag.RefreshToken = authorizationState.RefreshToken; ViewBag.Action = Request.Path;}//refresh tokenvar state = new AuthorizationState { AccessToken = Request.Form["AccessToken"], RefreshToken = Request.Form["RefreshToken"]};if (webServerClient.RefreshAuthorization(state)) { ViewBag.AccessToken = state.AccessToken; ViewBag.RefreshToken = state.RefreshToken;}// call protected user resourcevar client = new HttpClient(webServerClient.CreateAuthorizingHandler(accessToken));var body = await client.GetStringAsync(new Uri(Paths.ResourceUserApiPath));ViewBag.ApiResponse = body;
隐式授权 (Implicit Grant)
隐式授权为已知的公开客户端优化, 用于客户端操作一个特定的重定向地址, 只能获取访问凭据 (access token) , 不支持刷新凭据 (refresh token) 。 客户端通常在浏览器内用 Javascript 实现。
因为是基于 HTTP 重定向的方式, 所以客户端必须能够操纵资源所有者的用户代理(通常是浏览器)并且能够接收从授权服务器重定向过来的请求。
与授权码授权方式不同的是, 客户端不需要为授权和访问凭据分别发送单独的请求, 可以直接从授权请求获取访问凭据。
隐式授权不包括客户端授权, 依赖资源所有者(用户)的现场判断以及客户端重定向地址, 由于访问凭据是在 URL 中编码的, 所以有可能会暴漏给用户或客户端上的其它应用。
由于这种授权方式一般是通过浏览器实现的, 所以就不用依赖 DotNetOpenAuth 了, 只需要 Javascript 就行了, 示例代码如下:
// index.htmlvar authorizeUri = '@(Paths.AuthorizePath)';var tokenUri = '@(Paths.TokenPath)';var apiUri = '@Paths.ResourceUserApiPath';var clientId = '@clientId';var returnUri = '@clientRedirectUrl';var nonce = 'my-nonce';$('#authorize').click(function () { // build redirect url var uri = addQueryString(authorizeUri, { 'client_id': clientId, 'redirect_uri': returnUri, 'state': nonce, 'scope': 'bio notes', 'response_type': 'token' }); // login callback window.oauth = {}; window.oauth.signin = function (data) { if (data.state !== nonce) { return; } $('#accessToken').val(data.access_token); } // open login.html in a new window. window.open(uri, 'authorize', 'width=640,height=480');});// add query string to urifunction addQueryString(uri, parameters) { var delimiter = (uri.indexOf('?') == -1) ? '?' : '&'; for (var parameterName in parameters) { var parameterValue = parameters[parameterName]; uri += delimiter + encodeURIComponent(parameterName) + '=' + encodeURIComponent(parameterValue); delimiter = '&'; } return uri;}// login.html// get fragment and call opener's signin function.var fragments = getFragment();if (window.opener && window.opener.oauth && window.opener.oauth.signin) { window.opener.oauth.signin(fragments);}window.close();// get fragment from window urifunction getFragment() { if (window.location.hash.indexOf("#") === 0) { return parseQueryString(window.location.hash.substr(1)); } else { return {}; }}// parse query string to object;function parseQueryString(queryString) { var data = {}, pairs, pair, separatorIndex, escapedKey, escapedValue, key, value; if (queryString === null) { return data; } pairs = queryString.split("&"); for (var i = 0; i < pairs.length; i++) { pair = pairs[i]; separatorIndex = pair.indexOf("="); if (separatorIndex === -1) { escapedKey = pair; escapedValue = null; } else { escapedKey = pair.substr(0, separatorIndex); escapedValue = pair.substr(separatorIndex + 1); } key = decodeURIComponent(escapedKey); value = decodeURIComponent(escapedValue); data[key] = value; } return data;}
资源所有者密码凭据授权 (Resource Owner Password Credentials Grant)
资源所有者密码凭据授权适用于那些被充分信任的应用, 比如设备操作系统或者权限很高的应用。 授权服务器启用这类授权是要格外注意, 只能在其它授权方式不能用的时候才使用这种授权方式。
这种授权方式适用于能够取得用户的凭据 (通常是通过可交互的表单) 的应用, 也可以用于迁移现有的那些需要直接授权 (HTTP Basic 或 Digest ) 的应用, 将保存的用户凭据改为保存访问凭据 (access token) 。
对于 DotNetOpenAuth 来说, 这种授权也是十分容易实现的, 示例代码如下:
// create auth server description var authServer = new AuthorizationServerDescription { AuthorizationEndpoint = new Uri(Paths.AuthorizePath), TokenEndpoint = new Uri(Paths.TokenPath)};// create web server clientvar webServerClient = new WebServerClient(authServer, clientId, clientSecret);// use user name and password to exchange access token;var state = webServerClient.ExchangeUserCredentialForToken( username, password, new[] {"scope1", "scope2", "scope3"});// get access token;var token = state.AccessToken;
客户端凭据授权 (Client Credentials Grant)
客户端凭据授权是指客户端可以只通过客户端自己的凭据 (client_id 和 client_secret) (或者其它方式的认证) 来获取访问凭据, 客户端可以根据自己的需要来访问受保护的资源, 或者资源所有者已经访问过认证服务器时, 才能使用这种授权方式。 只有对完全受信任的客户端才能使用这种授权方式, 因为对受保护的资源方来说, 认证信息的内容是客户端程序的凭据, 而不是资源所有者的凭据。
DotNetOpenAuth 也支持这种授权方式, 示例代码如下:
// create auth server description var authServer = new AuthorizationServerDescription { AuthorizationEndpoint = new Uri(Paths.AuthorizePath), TokenEndpoint = new Uri(Paths.TokenPath)};// create web server clientvar webServerClient = new WebServerClient(authServer, clientId, clientSecret);// get client access token;var state = webServerClient.GetClientAccessToken( new[] { "test1", "test2", "test3" });// get access token;var token = state.AccessToken;
使用访问凭据访问受保护的资源
上面介绍的都是如何取得访问凭据 (access_token) , 拿到了访问凭据之后如何来使用呢? 对于使用微软的 OWIN 中间件 Microsoft.Owin.Security.OAuth 搭建的服务器来说, 需要设置 HTTP 请求的 Authorization 标头为 Bearer {access_token}
就可以了, 这个属于 OAuth 的规范之内了, 示例代码如下:
使用 jQuery 的 Ajax 请求时, 示例代码如下:
var accessToken = '@AccessToken';$.ajax({ url: '@ResourcePath', beforeSend: function(jqr) { jqr.setRequestHeader('Authorization', 'Bearer ' + accessToken); }}).done(function (data) { // other code here.});
使用其它语言的代码与上面的 js 代码大同小异,上面只是一些代码片段, 在 github 上有完整的项目代码, 不清楚的地方可以直接查看源代码。
推荐本站淘宝优惠价购买喜欢的宝贝:
本文链接:https://sg.hqyman.cn/post/3904.html 非本站原创文章欢迎转载,原创文章需保留本站地址!
休息一下~~