├── .gitignore
├── LICENSE
├── README.md
├── pom.xml
└── src
├── main
├── java
│ └── com
│ │ └── xiaomi
│ │ └── passport
│ │ ├── api
│ │ ├── AuthorizationCodeGrantHelper.java
│ │ ├── ImplicitGrantHelper.java
│ │ ├── OpenApiHelper.java
│ │ └── RefreshAccessTokenHelper.java
│ │ ├── common
│ │ ├── GrantType.java
│ │ ├── HttpMethod.java
│ │ ├── OAuthHttpClient.java
│ │ └── ResponseType.java
│ │ ├── constant
│ │ └── GlobalConstants.java
│ │ ├── exception
│ │ └── OAuthSdkException.java
│ │ ├── pojo
│ │ ├── AccessToken.java
│ │ ├── Client.java
│ │ └── UserProfile.java
│ │ └── util
│ │ ├── AuthorizeUrlUtils.java
│ │ ├── CommonUtils.java
│ │ └── HttpResponseUtils.java
└── resources
│ └── log4j.xml
└── test
├── java
└── com
│ └── xiaomi
│ └── passport
│ ├── api
│ ├── AuthorizationCodeGrantHelperTest.java
│ ├── ImplicitGrantHelperTest.java
│ ├── OpenApiHelperTest.java
│ └── RefreshAccessTokenHelperTest.java
│ └── utils
│ └── OAuthTestUtils.java
└── resources
└── log4j.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | *.iml
3 | .settings
4 | .project
5 | .classpath
6 |
7 | # Compiled class file
8 | *.class
9 |
10 | # Log file
11 | *.log
12 |
13 | # BlueJ files
14 | *.ctxt
15 |
16 | # Mobile Tools for Java (J2ME)
17 | .mtj.tmp/
18 |
19 | # Package Files #
20 | *.jar
21 | *.war
22 | *.ear
23 | *.zip
24 | *.tar.gz
25 | *.rar
26 |
27 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
28 | hs_err_pid*
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 小米帐号开放平台 OAuth JAVA SDK 使用说明
2 |
3 | 说明:本文档适用于 1.0 以上 sdk 版本,历史版本可以 [点此查看](https://github.com/xiaomi-passport/oauth-java-sdk/wiki/v0.0.1)。
4 |
5 | > - jdk version : 1.6+
6 | > - maven version : 3.0+
7 |
8 | ## 一. SDK 结构说明
9 | > - com.xiaomi.passport.api.AuthorizationCodeGrantHelper : 授权码模式授权类
10 | > - com.xiaomi.passport.api.RefreshAccessTokenHelper : 刷新访问令牌操作类
11 | > - com.xiaomi.passport.api.OpenApiHelper : 开放数据接口操作类
12 |
13 | ## 二. SDK 使用说明
14 |
15 | ### 2.1 授权接口
16 |
17 | 小米帐号开放针对第三方 APP 仅支持 __授权码模式__ 授权方式,请参考 ‘[OAuth2.0协议原理与实现:协议原理](https://dev.mi.com/console/doc/detail?pId=711)’
18 |
19 | #### 2.1.1 授权码模式授权
20 |
21 | 授权码模式 SDK 接口位于 com.xiaomi.passport.api.AuthorizationCodeGrantHelper 类中,接口使用说明如下:
22 |
23 | - __获取授权码 : getAuthorizationCode__
24 |
25 | 获取授权码授权码模式两步流程的第一步,因为该过程需要与用户交互(即询问用户是否确认授权),所以整个过程无法仅仅通过程序完成。我们在方法中仅仅描述了获取授权码的概要流程,并推荐您参考 ‘[授权码授权模式](https://dev.mi.com/console/doc/detail?pId=707)’ 接口 1 中的说明构建请求地址,请求过程中需要用户确认授权,我们最终会以参数的形式将授权码追加到回调地址后面,并以 302 回调形式下发。
26 |
27 | - __获取访问令牌 : getAccessTokenByCode__
28 |
29 | 在您通过上一步拿到授权之后,接下去可以通过授权码请求小米授权服务器下发访问令牌,SDK 使用示例:
30 |
31 | ```java
32 | // 构建您的 APP 信息
33 | Client client = new Client();
34 | // 构建授权请求实例
35 | AuthorizationCodeGrantHelper helper = new AuthorizationCodeGrantHelper(client);
36 | // 利用授权码请求下发访问令牌
37 | AccessToken accessToken = helper.getAccessTokenByCode(code);
38 | ```
39 |
40 | 如果请求成功,则会返回 `AccessToken` 对象,对象属性含义可以参考 ‘[授权码授权模式](https://dev.mi.com/console/doc/detail?pId=707)’ 接口 2 中的释义。
41 |
42 | 如果请求失败,则会抛出 `OAuthSdkException`,异常中包含响应的错误码和描述信息。
43 |
44 | 注意事项:
45 |
46 | 1. 授权码生命周期为 10 分钟,且只能被使用一次
47 | 2. 访问令牌和刷新令牌最终都应该由服务端持有
48 | 2. 授权过程中需要传递的 `client_secret` 只能由服务端持有,因为使用不当造成的用户数据泄露由第三方企业负责
49 |
50 | #### 2.1.2 刷新访问令牌
51 |
52 | 授权码模式下发令牌时,默认会同时下发访问令牌和刷新令牌,刷新令牌相对于访问令牌具备较长的生命周期,可以在访问令牌过期时更新访问令牌。
53 |
54 | SDK 使用示例:
55 |
56 | ```java
57 | // 构建您的 APP 信息
58 | Client client = new Client();
59 | // 构建刷新访问令牌请求实例
60 | RefreshAccessTokenHelper helper = new RefreshAccessTokenHelper(client);
61 | // 利用刷新令牌更新访问令牌
62 | AccessToken accessToken = helper.refreshAccessToken(refreshToken);
63 | ```
64 |
65 | 如果请求成功,则会返回 `AccessToken` 对象,对象属性含义可以参考 ‘[访问令牌更新](https://dev.mi.com/console/doc/detail?pId=712)’ 接口 1 中的释义。
66 |
67 | 如果请求失败,则会抛出 `OAuthSdkException`,异常中包含响应的错误码和描述信息。
68 |
69 | ### 2.2 开放数据接口
70 |
71 | 授权完成之后,您可以通过手上持有的访问令牌请求权限范围内的小米开放数据和服务,帐号这边开放了用户资料,包括用户的基本资料等信息。
72 |
73 | SDK 使用示例:
74 |
75 | ```java
76 | // 构建您的 APP 信息
77 | Client client = new Client();
78 | // 准备访问令牌
79 | String accessToken = "your access token here";
80 | // 构建开放数据请求实例
81 | OpenApiHelper helper = new OpenApiHelper(client.getId(), accessToken);
82 |
83 | // 1. 获取用户名片
84 | UserProfile profile = helper.getUserProfile();
85 |
86 | // 2. 获取用户OpenID
87 | String openId = helper.getOpenId()
88 |
89 | ```
90 |
91 | ## 相关文档
92 |
93 | 1. [小米帐号开放平台接入指南](https://dev.mi.com/console/doc/detail?pId=897)
94 | 2. [OAuth 2.0 协议原理与实现](https://dev.mi.com/console/doc/detail?pId=711)
95 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 | com.xiaomi.passport
5 | oauth-java-sdk
6 | 1.0.0-SNAPSHOT
7 |
8 |
9 |
10 |
11 | org.apache.commons
12 | commons-lang3
13 | 3.5
14 |
15 |
16 | commons-lang
17 | commons-lang
18 | 2.6
19 |
20 |
21 | commons-collections
22 | commons-collections
23 | 3.2.2
24 |
25 |
26 | commons-io
27 | commons-io
28 | 2.5
29 |
30 |
31 | commons-validator
32 | commons-validator
33 | 1.6
34 |
35 |
36 |
37 | org.apache.httpcomponents
38 | httpclient
39 | 4.5.3
40 |
41 |
42 | org.apache.httpcomponents
43 | httpclient-cache
44 | 4.5.3
45 |
46 |
47 |
48 | org.slf4j
49 | slf4j-api
50 | 1.7.25
51 |
52 |
53 | org.slf4j
54 | slf4j-log4j12
55 | 1.7.25
56 |
57 |
58 |
59 | net.sf.json-lib
60 | json-lib
61 | 2.4
62 |
63 |
64 | net.sf.ezmorph
65 | ezmorph
66 | 1.0.6
67 |
68 |
69 |
70 | junit
71 | junit
72 | 4.12
73 | test
74 |
75 |
76 |
77 |
78 |
79 |
80 | org.apache.maven.plugins
81 | maven-compiler-plugin
82 | 3.6.1
83 |
84 | 1.6
85 | 1.6
86 |
87 |
88 |
89 | org.apache.maven.plugins
90 | maven-surefire-plugin
91 | 2.19.1
92 |
93 | true
94 |
95 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/src/main/java/com/xiaomi/passport/api/AuthorizationCodeGrantHelper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2017 xiaomi.com, Inc. All Rights Reserved
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.xiaomi.passport.api;
18 |
19 | import com.xiaomi.passport.common.GrantType;
20 | import com.xiaomi.passport.common.OAuthHttpClient;
21 | import com.xiaomi.passport.common.ResponseType;
22 | import com.xiaomi.passport.constant.GlobalConstants;
23 | import com.xiaomi.passport.exception.OAuthSdkException;
24 | import com.xiaomi.passport.pojo.AccessToken;
25 | import com.xiaomi.passport.pojo.Client;
26 | import com.xiaomi.passport.util.AuthorizeUrlUtils;
27 | import com.xiaomi.passport.util.HttpResponseUtils;
28 | import net.sf.json.JSONObject;
29 | import net.sf.json.util.JSONUtils;
30 | import org.apache.commons.lang3.ArrayUtils;
31 | import org.apache.commons.lang3.StringUtils;
32 | import org.apache.http.HttpResponse;
33 | import org.apache.http.NameValuePair;
34 | import org.apache.http.message.BasicNameValuePair;
35 | import org.slf4j.Logger;
36 | import org.slf4j.LoggerFactory;
37 |
38 | import java.util.ArrayList;
39 | import java.util.List;
40 |
41 | /**
42 | * Authorization code grant type
43 | *
44 | * @author zhenchao.wang 2017-04-14 13:48:14
45 | * @version 1.0.0
46 | */
47 | public class AuthorizationCodeGrantHelper implements GlobalConstants {
48 |
49 | private static final Logger log = LoggerFactory.getLogger(AuthorizationCodeGrantHelper.class);
50 |
51 | private Client client;
52 |
53 | private OAuthHttpClient httpClient;
54 |
55 | public AuthorizationCodeGrantHelper(Client client) {
56 | this.client = client;
57 | this.httpClient = new OAuthHttpClient();
58 | }
59 |
60 | public AuthorizationCodeGrantHelper(Client client, OAuthHttpClient httpClient) {
61 | this.client = client;
62 | this.httpClient = httpClient;
63 | }
64 |
65 | /**
66 | * get authorization code
67 | * since issue authorization code process must interact with resource owner, so we can't do it only in program
68 | *
69 | * @param skipConfirm
70 | * @param state
71 | * @return
72 | * @deprecated this method is error to use
73 | */
74 | @Deprecated
75 | public String getAuthorizationCode(boolean skipConfirm, String state) {
76 |
77 | log.debug("Get authorization code...");
78 |
79 | // prepare params
80 | List params = new ArrayList();
81 | params.add(new BasicNameValuePair(CLIENT_ID, String.valueOf(client.getId())));
82 | params.add(new BasicNameValuePair(REDIRECT_URI, client.getRedirectUri()));
83 | params.add(new BasicNameValuePair(RESPONSE_TYPE, ResponseType.AUTHORIZATION_CODE.getType()));
84 | if (ArrayUtils.isNotEmpty(client.getScopes())) {
85 | params.add(new BasicNameValuePair(SCOPE, StringUtils.join(client.getScopes(), " ")));
86 | }
87 | if (StringUtils.isNotBlank(state)) {
88 | params.add(new BasicNameValuePair(STATE, state));
89 | }
90 | params.add(new BasicNameValuePair(SKIP_CONFIRM, String.valueOf(skipConfirm)));
91 |
92 | /*
93 | * interactive process:
94 | *
95 | * 1. request https://account.xiaomi.com/oauth2/authorize with params
96 | * 2. interact with resource owner and authorize
97 | * 3. issued authorization code as 302 redirect
98 | *
99 | */
100 |
101 | return StringUtils.EMPTY;
102 | }
103 |
104 | /**
105 | * get access code by authorization code
106 | *
107 | * @param code authorization code
108 | * @return
109 | * @throws OAuthSdkException when encounter error response
110 | */
111 | public AccessToken getAccessTokenByCode(String code) throws OAuthSdkException {
112 |
113 | log.debug("Get access token by code...");
114 |
115 | // prepare params
116 | List params = new ArrayList();
117 | params.add(new BasicNameValuePair(CLIENT_ID, String.valueOf(client.getId())));
118 | params.add(new BasicNameValuePair(REDIRECT_URI, client.getRedirectUri()));
119 | params.add(new BasicNameValuePair(CLIENT_SECRET, client.getSecret()));
120 | params.add(new BasicNameValuePair(GRANT_TYPE, GrantType.AUTHORIZATION_CODE.getType()));
121 | params.add(new BasicNameValuePair(CODE, code));
122 | params.add(new BasicNameValuePair(TOKEN_TYPE, AccessToken.TokenType.MAC.getType()));
123 |
124 | HttpResponse response = httpClient.get(AuthorizeUrlUtils.getTokenUrl(), params);
125 | log.debug("Get authorization code access token response[{}]", response);
126 |
127 | String entityContent = HttpResponseUtils.getEntityContent(response);
128 | if (StringUtils.isBlank(entityContent) || !JSONUtils.mayBeJSON(entityContent)) {
129 | log.error("The response[{}] is not json format!", entityContent);
130 | throw new OAuthSdkException("The response is not json format");
131 | }
132 |
133 | JSONObject json = JSONObject.fromObject(entityContent);
134 | if (json.has("access_token")) {
135 | log.debug("Get authorization code access token json result[{}]", json);
136 | return new AccessToken(json);
137 | }
138 |
139 | // error response
140 | int errorCode = json.optInt("error", -1);
141 | String errorDesc = json.optString("error_description", StringUtils.EMPTY);
142 | log.error("Get authorization code access token error, error info [code={}, desc={}]!", errorCode, errorDesc);
143 | throw new OAuthSdkException("Get authorization code access token error!", errorCode, errorDesc);
144 | }
145 |
146 | }
147 |
--------------------------------------------------------------------------------
/src/main/java/com/xiaomi/passport/api/ImplicitGrantHelper.java:
--------------------------------------------------------------------------------
1 | package com.xiaomi.passport.api;
2 |
3 | import com.xiaomi.passport.common.OAuthHttpClient;
4 | import com.xiaomi.passport.common.ResponseType;
5 | import com.xiaomi.passport.constant.GlobalConstants;
6 | import com.xiaomi.passport.pojo.AccessToken;
7 | import com.xiaomi.passport.pojo.Client;
8 | import org.apache.commons.lang3.ArrayUtils;
9 | import org.apache.commons.lang3.StringUtils;
10 | import org.apache.http.NameValuePair;
11 | import org.apache.http.message.BasicNameValuePair;
12 | import org.slf4j.Logger;
13 | import org.slf4j.LoggerFactory;
14 |
15 | import java.util.ArrayList;
16 | import java.util.List;
17 |
18 | /**
19 | * implicit grant type
20 | *
21 | * @author zhenchao.wang 2017-04-20 18:14
22 | * @version 1.0.0
23 | */
24 | public class ImplicitGrantHelper implements GlobalConstants {
25 |
26 | private static final Logger log = LoggerFactory.getLogger(RefreshAccessTokenHelper.class);
27 |
28 | private Client client;
29 |
30 | private OAuthHttpClient httpClient;
31 |
32 | public ImplicitGrantHelper(Client client) {
33 | this.client = client;
34 | }
35 |
36 | public ImplicitGrantHelper(Client client, OAuthHttpClient httpClient) {
37 | this.client = client;
38 | this.httpClient = httpClient;
39 | }
40 |
41 | /**
42 | * get implicit access token
43 | * since issue authorization code process must interact with resource owner, so we can't do it only in program
44 | *
45 | * @param skipConfirm
46 | * @param state
47 | * @return
48 | * @deprecated this method is error to use
49 | */
50 | @Deprecated
51 | public AccessToken getImplicitAccessToken(boolean skipConfirm, String state) {
52 |
53 | log.debug("Get implicit access token...");
54 |
55 | // prepare params
56 | List params = new ArrayList();
57 | params.add(new BasicNameValuePair(CLIENT_ID, String.valueOf(client.getId())));
58 | params.add(new BasicNameValuePair(REDIRECT_URI, client.getRedirectUri()));
59 | params.add(new BasicNameValuePair(RESPONSE_TYPE, ResponseType.IMPLICIT.getType()));
60 | if (ArrayUtils.isNotEmpty(client.getScopes())) {
61 | params.add(new BasicNameValuePair(SCOPE, StringUtils.join(client.getScopes(), " ")));
62 | }
63 | if (StringUtils.isNotBlank(state)) {
64 | params.add(new BasicNameValuePair(STATE, state));
65 | }
66 | params.add(new BasicNameValuePair(SKIP_CONFIRM, String.valueOf(skipConfirm)));
67 |
68 | /*
69 | * interactive process:
70 | *
71 | * 1. request https://account.xiaomi.com/oauth2/authorize with params
72 | * 2. interact with resource owner and authorize
73 | * 3. issued implicit access token as url fragment
74 | *
75 | */
76 |
77 | return null;
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/main/java/com/xiaomi/passport/api/OpenApiHelper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2017 xiaomi.com, Inc. All Rights Reserved
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.xiaomi.passport.api;
18 |
19 | import com.xiaomi.passport.common.HttpMethod;
20 | import com.xiaomi.passport.common.OAuthHttpClient;
21 | import com.xiaomi.passport.constant.GlobalConstants;
22 | import com.xiaomi.passport.exception.OAuthSdkException;
23 | import com.xiaomi.passport.pojo.UserProfile;
24 | import com.xiaomi.passport.util.HttpResponseUtils;
25 | import net.sf.json.JSONObject;
26 | import net.sf.json.util.JSONUtils;
27 | import org.apache.commons.collections.CollectionUtils;
28 | import org.apache.commons.lang3.StringUtils;
29 | import org.apache.commons.lang3.tuple.Pair;
30 | import org.apache.http.Header;
31 | import org.apache.http.HttpResponse;
32 | import org.apache.http.NameValuePair;
33 | import org.apache.http.message.BasicNameValuePair;
34 | import org.slf4j.Logger;
35 | import org.slf4j.LoggerFactory;
36 |
37 | import java.util.ArrayList;
38 | import java.util.List;
39 |
40 | /**
41 | * xiaomi passport open api request helper
42 | * docs: https://dev.mi.com/docs/passport/open-api/
43 | *
44 | * @author zhenchao.wang 2017-04-14 16:29:27
45 | * @version 1.0.0
46 | */
47 | public class OpenApiHelper implements GlobalConstants {
48 |
49 | protected static final Logger log = LoggerFactory.getLogger(OpenApiHelper.class);
50 |
51 | protected static final String OPEN_CLIENT_ID = "clientId";
52 |
53 | protected static final String ACCESS_TOKEN = "token";
54 |
55 | protected long clientId;
56 |
57 | protected String accessToken;
58 |
59 | protected OAuthHttpClient httpClient;
60 |
61 | public OpenApiHelper(long clientId, String accessToken) {
62 | super();
63 | this.accessToken = accessToken;
64 | this.clientId = clientId;
65 | this.httpClient = new OAuthHttpClient();
66 | }
67 |
68 | public OpenApiHelper(long clientId, String accessToken, OAuthHttpClient httpClient) {
69 | super();
70 | this.clientId = clientId;
71 | this.accessToken = accessToken;
72 | this.httpClient = httpClient;
73 | }
74 |
75 | /**
76 | * get user profile by access token and client id
77 | *
78 | * @return
79 | * @throws OAuthSdkException
80 | */
81 | public UserProfile getUserProfile() throws OAuthSdkException {
82 | List params = new ArrayList();
83 | params.add(new BasicNameValuePair(OPEN_CLIENT_ID, String.valueOf(clientId)));
84 | params.add(new BasicNameValuePair(ACCESS_TOKEN, accessToken));
85 | JSONObject json = this.request(HttpMethod.GET, USER_PROFILE_PATH, params);
86 | if (!this.isSuccessResult(json)) {
87 | // error response
88 | int errorCode = json.optInt("code", -1);
89 | String errorDesc = json.optString("description", StringUtils.EMPTY);
90 | log.error("Get user profile error, error info[code={}, desc={}]", errorCode, errorDesc);
91 | throw new OAuthSdkException("get user profile error", errorCode, errorDesc);
92 | }
93 | String data = json.optString("data", StringUtils.EMPTY);
94 | if (StringUtils.isBlank(data) || !JSONUtils.mayBeJSON(data)) {
95 | log.error("Response json missing 'data' element or not json format, data[{}]", data);
96 | }
97 | log.debug("Get user profile json response [{}]", data);
98 | return new UserProfile(JSONObject.fromObject(data));
99 | }
100 |
101 | /**
102 | * get open id by access token and client id
103 | *
104 | * @return
105 | * @throws OAuthSdkException
106 | */
107 | public String getOpenId() throws OAuthSdkException {
108 | List params = new ArrayList();
109 | params.add(new BasicNameValuePair(OPEN_CLIENT_ID, String.valueOf(clientId)));
110 | params.add(new BasicNameValuePair(ACCESS_TOKEN, accessToken));
111 | JSONObject json = this.request(HttpMethod.GET, OPEN_ID_PATH, params);
112 | if (!this.isSuccessResult(json)) {
113 | // error response
114 | int errorCode = json.optInt("code", -1);
115 | String errorDesc = json.optString("description", StringUtils.EMPTY);
116 | log.error("Get user open id error, error info[code={}, desc={}]", errorCode, errorDesc);
117 | throw new OAuthSdkException("get user open id error", errorCode, errorDesc);
118 | }
119 | String data = json.optString("data", StringUtils.EMPTY);
120 | if (StringUtils.isBlank(data) || !JSONUtils.mayBeJSON(data)) {
121 | log.error("Response json missing 'data' element or not json format, data[{}]", data);
122 | }
123 | log.debug("Get open id json response [{}]", data);
124 | return JSONObject.fromObject(data).optString(OPEN_ID, StringUtils.EMPTY);
125 | }
126 |
127 | /**
128 | * get phone number and email address by access token and client id
129 | *
130 | * @return
131 | * @throws OAuthSdkException
132 | */
133 | public Pair getPhoneAndEmail() throws OAuthSdkException {
134 | List params = new ArrayList();
135 | params.add(new BasicNameValuePair(OPEN_CLIENT_ID, String.valueOf(clientId)));
136 | params.add(new BasicNameValuePair(ACCESS_TOKEN, accessToken));
137 | JSONObject json = this.request(HttpMethod.GET, PHONE_AND_EMAIL_PATH, params);
138 | if (!this.isSuccessResult(json)) {
139 | // error response
140 | int errorCode = json.optInt("code", -1);
141 | String errorDesc = json.optString("description", StringUtils.EMPTY);
142 | log.error("Get user phone number and email address error, error info[code={}, desc={}]", errorCode, errorDesc);
143 | throw new OAuthSdkException("get user phone and email error", errorCode, errorDesc);
144 | }
145 | JSONObject data = json.optJSONObject("data");
146 | log.debug("Get user phone and email json response [{}]", data);
147 | return Pair.of(data.optString("phone", StringUtils.EMPTY), data.optString("email", StringUtils.EMPTY));
148 | }
149 |
150 | /**
151 | * get user miliao friend id list by access token and client id
152 | *
153 | * @return
154 | * @throws OAuthSdkException
155 | */
156 | public List getFriendIdList() throws OAuthSdkException {
157 | List params = new ArrayList();
158 | params.add(new BasicNameValuePair(OPEN_CLIENT_ID, String.valueOf(clientId)));
159 | params.add(new BasicNameValuePair(ACCESS_TOKEN, accessToken));
160 | JSONObject json = this.request(HttpMethod.GET, USER_REALTION_PATH, params);
161 | if (!this.isSuccessResult(json)) {
162 | // error response
163 | int errorCode = json.optInt("code", -1);
164 | String errorDesc = json.optString("description", StringUtils.EMPTY);
165 | log.error("Get user miliao friend list error, error info[code={}, desc={}]", errorCode, errorDesc);
166 | throw new OAuthSdkException("get user miliao friend list error", errorCode, errorDesc);
167 | }
168 | List ids = new ArrayList();
169 | String friends = json.optJSONObject("data").optString("friends", StringUtils.EMPTY);
170 | log.debug("Get user miliao friends result [{}]", friends);
171 | if (StringUtils.isBlank(friends)) {
172 | return ids;
173 | }
174 | String[] elements = friends.split(",");
175 | for (final String element : elements) {
176 | ids.add(Long.valueOf(element));
177 | }
178 | return ids;
179 | }
180 |
181 | /**
182 | * send request to specify url with params and expected json response
183 | *
184 | * @param path the path part of api url, /user/profile etc.
185 | * @param method GET or POST
186 | * @param params query params
187 | * @return json response data
188 | * @throws OAuthSdkException
189 | */
190 | protected JSONObject request(HttpMethod method, String path, List params) throws OAuthSdkException {
191 | return this.request(method, path, params, null);
192 | }
193 |
194 | /**
195 | * send request to specify url with params and expected json response
196 | *
197 | * @param path the path part of api url, /user/profile etc.
198 | * @param method GET or POST
199 | * @param params query params
200 | * @param headers query http headers
201 | * @return json response data
202 | * @throws OAuthSdkException
203 | */
204 | protected JSONObject request(
205 | HttpMethod method, String path, List params, List headers) throws OAuthSdkException {
206 |
207 | // params format
208 | List paramList = (null == params) ? new ArrayList() : params;
209 | List headerList = (null == headers) ? new ArrayList() : headers;
210 |
211 | Header[] headerArray = new Header[0];
212 | if (CollectionUtils.isEmpty(headers)) {
213 | headerArray = new Header[headerList.size()];
214 | headerArray = headerList.toArray(headerArray);
215 | }
216 |
217 | HttpResponse response;
218 | if (HttpMethod.GET.equals(method)) {
219 | response = httpClient.get(GlobalConstants.OPEN_API_HOST + path, params, headerArray);
220 | } else if (HttpMethod.POST.equals(method)) {
221 | response = httpClient.post(GlobalConstants.OPEN_API_HOST + path, params, headerArray);
222 | } else {
223 | log.error("The http method [{}] is unsupported!", method);
224 | throw new OAuthSdkException("unsupported http method");
225 | }
226 |
227 | String entityContent = HttpResponseUtils.getEntityContent(response);
228 | if (StringUtils.isBlank(entityContent) || !JSONUtils.mayBeJSON(entityContent)) {
229 | log.error("The response data is not json format, data[{}]", entityContent);
230 | throw new OAuthSdkException("response data is not json");
231 | }
232 |
233 | return JSONObject.fromObject(entityContent);
234 | }
235 |
236 | /**
237 | * validate if success response
238 | *
239 | * @param json
240 | * @return
241 | */
242 | private boolean isSuccessResult(JSONObject json) {
243 | // never be null
244 | return (null != json && "ok".equalsIgnoreCase(json.optString("result", StringUtils.EMPTY)));
245 | }
246 |
247 | }
248 |
--------------------------------------------------------------------------------
/src/main/java/com/xiaomi/passport/api/RefreshAccessTokenHelper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2017 xiaomi.com, Inc. All Rights Reserved
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.xiaomi.passport.api;
18 |
19 | import com.xiaomi.passport.common.GrantType;
20 | import com.xiaomi.passport.common.OAuthHttpClient;
21 | import com.xiaomi.passport.constant.GlobalConstants;
22 | import com.xiaomi.passport.exception.OAuthSdkException;
23 | import com.xiaomi.passport.pojo.AccessToken;
24 | import com.xiaomi.passport.pojo.Client;
25 | import com.xiaomi.passport.util.AuthorizeUrlUtils;
26 | import com.xiaomi.passport.util.HttpResponseUtils;
27 | import net.sf.json.JSONObject;
28 | import net.sf.json.util.JSONUtils;
29 | import org.apache.commons.lang3.StringUtils;
30 | import org.apache.http.HttpResponse;
31 | import org.apache.http.NameValuePair;
32 | import org.apache.http.message.BasicNameValuePair;
33 | import org.slf4j.Logger;
34 | import org.slf4j.LoggerFactory;
35 |
36 | import java.util.ArrayList;
37 | import java.util.List;
38 |
39 | /**
40 | * refresh access token
41 | *
42 | * @author zhenchao.wang 2017-04-20 18:12
43 | * @version 1.0.0
44 | */
45 | public class RefreshAccessTokenHelper implements GlobalConstants {
46 |
47 | private static final Logger log = LoggerFactory.getLogger(RefreshAccessTokenHelper.class);
48 |
49 | private Client client;
50 |
51 | private OAuthHttpClient httpClient;
52 |
53 | public RefreshAccessTokenHelper(Client client) {
54 | this.client = client;
55 | this.httpClient = new OAuthHttpClient();
56 | }
57 |
58 | public RefreshAccessTokenHelper(Client client, OAuthHttpClient httpClient) {
59 | this.client = client;
60 | this.httpClient = httpClient;
61 | }
62 |
63 | /**
64 | * refresh access token by refresh token
65 | *
66 | * @param refreshToken
67 | * @return
68 | * @throws OAuthSdkException
69 | */
70 | public AccessToken refreshAccessToken(String refreshToken) throws OAuthSdkException {
71 |
72 | // prepare params
73 | List params = new ArrayList();
74 | params.add(new BasicNameValuePair(CLIENT_ID, String.valueOf(client.getId())));
75 | params.add(new BasicNameValuePair(REDIRECT_URI, client.getRedirectUri()));
76 | params.add(new BasicNameValuePair(CLIENT_SECRET, client.getSecret()));
77 | params.add(new BasicNameValuePair(GRANT_TYPE, GrantType.REFRESH_TOKEN.getType()));
78 | params.add(new BasicNameValuePair(TOKEN_TYPE, AccessToken.TokenType.MAC.getType()));
79 | params.add(new BasicNameValuePair(REFRESH_TOKEN, refreshToken));
80 |
81 | HttpResponse response = httpClient.get(AuthorizeUrlUtils.getTokenUrl(), params);
82 | log.debug("Refresh access token response[{}]", response);
83 |
84 | String entityContent = HttpResponseUtils.getEntityContent(response);
85 | if (StringUtils.isBlank(entityContent) || !JSONUtils.mayBeJSON(entityContent)) {
86 | log.error("The refresh token response[{}] is not json format!", entityContent);
87 | throw new OAuthSdkException("The refresh token response is not json format");
88 | }
89 |
90 | JSONObject json = JSONObject.fromObject(entityContent);
91 | if (json.has("access_token")) {
92 | log.debug("Refresh access token json result[{}]", json);
93 | return new AccessToken(json);
94 | }
95 |
96 | // error response
97 | int errorCode = json.optInt("error", -1);
98 | String errorDesc = json.optString("error_description", StringUtils.EMPTY);
99 | log.error("Refresh access token error, error info [code={}, desc={}]!", errorCode, errorDesc);
100 | throw new OAuthSdkException("Refresh access token error!", errorCode, errorDesc);
101 | }
102 |
103 | }
104 |
--------------------------------------------------------------------------------
/src/main/java/com/xiaomi/passport/common/GrantType.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2017 xiaomi.com, Inc. All Rights Reserved
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.xiaomi.passport.common;
18 |
19 | /**
20 | * oauth 2.0 grant type
21 | *
22 | * @author zhenchao.wang 2017-04-20 17:31
23 | * @version 1.0.0
24 | */
25 | public enum GrantType {
26 |
27 | AUTHORIZATION_CODE("authorization_code"),
28 |
29 | PASSWORD_CREDENTIALS("password"),
30 |
31 | CLIENT_CREDENTIALS("client_credentials"),
32 |
33 | REFRESH_TOKEN("refresh_token");
34 |
35 | private String type;
36 |
37 | GrantType(String type) {
38 | this.type = type;
39 | }
40 |
41 | public String getType() {
42 | return type;
43 | }
44 |
45 | /**
46 | * tell whether {@param grantType} is valid
47 | *
48 | * @param grantType
49 | * @return
50 | */
51 | public static boolean isValid(String grantType) {
52 | return AUTHORIZATION_CODE.getType().equals(grantType)
53 | || PASSWORD_CREDENTIALS.getType().equals(grantType) || CLIENT_CREDENTIALS.getType().equals(grantType);
54 | }
55 |
56 | /**
57 | * change string param to {@code GrantType} object
58 | *
59 | * @param grantType
60 | * @return
61 | */
62 | public static GrantType toGrantType(String grantType) {
63 | for (final GrantType type : GrantType.values()) {
64 | if (type.getType().equals(grantType)) {
65 | return type;
66 | }
67 | }
68 | return null;
69 | }
70 | }
--------------------------------------------------------------------------------
/src/main/java/com/xiaomi/passport/common/HttpMethod.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2017 xiaomi.com, Inc. All Rights Reserved
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.xiaomi.passport.common;
18 |
19 | /**
20 | * http method
21 | *
22 | * @author zhenchao.wang 2017-04-14 16:36
23 | * @version 1.0.0
24 | */
25 | public enum HttpMethod {
26 |
27 | GET("GET"),
28 |
29 | POST("POST");
30 |
31 | private String value;
32 |
33 | HttpMethod(String value) {
34 | this.value = value;
35 | }
36 |
37 | public String getValue() {
38 | return value;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/com/xiaomi/passport/common/OAuthHttpClient.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2017 xiaomi.com, Inc. All Rights Reserved
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.xiaomi.passport.common;
18 |
19 | import com.xiaomi.passport.exception.OAuthSdkException;
20 | import org.apache.commons.collections.CollectionUtils;
21 | import org.apache.commons.lang3.ArrayUtils;
22 | import org.apache.http.Header;
23 | import org.apache.http.HttpResponse;
24 | import org.apache.http.NameValuePair;
25 | import org.apache.http.client.HttpClient;
26 | import org.apache.http.client.config.CookieSpecs;
27 | import org.apache.http.client.config.RequestConfig;
28 | import org.apache.http.client.methods.HttpGet;
29 | import org.apache.http.client.methods.HttpPost;
30 | import org.apache.http.client.methods.HttpUriRequest;
31 | import org.apache.http.client.utils.URIBuilder;
32 | import org.apache.http.config.RegistryBuilder;
33 | import org.apache.http.conn.socket.ConnectionSocketFactory;
34 | import org.apache.http.conn.socket.PlainConnectionSocketFactory;
35 | import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
36 | import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
37 | import org.apache.http.impl.client.HttpClientBuilder;
38 | import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
39 | import org.slf4j.Logger;
40 | import org.slf4j.LoggerFactory;
41 |
42 | import java.io.IOException;
43 | import java.net.URISyntaxException;
44 | import java.util.ArrayList;
45 | import java.util.List;
46 |
47 | /**
48 | * oauth http request client
49 | *
50 | * @author zhenchao.wang 2017-04-14 16:24:51
51 | * @version 1.0.0
52 | */
53 | public class OAuthHttpClient {
54 |
55 | private final Logger log = LoggerFactory.getLogger(OAuthHttpClient.class);
56 |
57 | private static final int CONNECT_TIMEOUT = 30 * 1000;
58 |
59 | private static final int SOCKET_TIMEOUT = 30 * 1000;
60 |
61 | private static final int MAX_CON_PER_HOST = 100;
62 |
63 | private HttpClient httpClient = null;
64 |
65 | public OAuthHttpClient() {
66 | this(MAX_CON_PER_HOST, CONNECT_TIMEOUT, SOCKET_TIMEOUT);
67 | }
68 |
69 | public OAuthHttpClient(int maxConnection, int connectTimeout, int socketTimeout) {
70 | PoolingHttpClientConnectionManager connectionManager =
71 | new PoolingHttpClientConnectionManager(RegistryBuilder.create()
72 | .register("http", PlainConnectionSocketFactory.getSocketFactory())
73 | .register("https", SSLConnectionSocketFactory.getSocketFactory())
74 | .build());
75 | // set max connection
76 | connectionManager.setMaxTotal(maxConnection);
77 |
78 | RequestConfig requestConfig = RequestConfig.custom()
79 | .setConnectTimeout(connectTimeout)
80 | .setSocketTimeout(socketTimeout)
81 | .setCookieSpec(CookieSpecs.IGNORE_COOKIES)
82 | .build();
83 |
84 | httpClient = HttpClientBuilder.create()
85 | .setConnectionManager(connectionManager)
86 | .setDefaultRequestConfig(requestConfig)
87 | .setRetryHandler(new DefaultHttpRequestRetryHandler(3, true))
88 | .build();
89 | }
90 |
91 | /**
92 | * get request without params
93 | *
94 | * @param url
95 | * @return
96 | * @throws OAuthSdkException
97 | */
98 | public HttpResponse get(String url) throws OAuthSdkException {
99 | return this.get(url, new ArrayList(), null);
100 | }
101 |
102 | /**
103 | * get request with params
104 | *
105 | * @param url
106 | * @param params
107 | * @return
108 | * @throws OAuthSdkException
109 | */
110 | public HttpResponse get(String url, List params) throws OAuthSdkException {
111 | return this.get(url, params, null);
112 | }
113 |
114 | /**
115 | * get request with params and headers
116 | *
117 | * @param url
118 | * @param params
119 | * @param headers
120 | * @return
121 | * @throws OAuthSdkException
122 | */
123 | public HttpResponse get(String url, List params, Header[] headers) throws OAuthSdkException {
124 | try {
125 | URIBuilder builder = new URIBuilder(url);
126 | if (CollectionUtils.isNotEmpty(params)) {
127 | builder.setParameters(params);
128 | }
129 | HttpUriRequest request = new HttpGet(builder.build());
130 | if (ArrayUtils.isNotEmpty(headers)) {
131 | request.setHeaders(headers);
132 | }
133 | return httpClient.execute(request);
134 | } catch (URISyntaxException e) {
135 | log.error("The uri[{}] is illegal!", url, e);
136 | throw new OAuthSdkException("url syntax exception", e);
137 | } catch (IOException e) {
138 | log.error("Execute get request url[{}] error!", url, e);
139 | throw new OAuthSdkException("execute get request error", e);
140 | }
141 | }
142 |
143 | /**
144 | * post request without params
145 | *
146 | * @param url
147 | * @return
148 | * @throws OAuthSdkException
149 | */
150 | public HttpResponse post(String url) throws OAuthSdkException {
151 | return this.post(url, new ArrayList(), null);
152 | }
153 |
154 | /**
155 | * post request with params
156 | *
157 | * @param url
158 | * @param params
159 | * @return
160 | * @throws OAuthSdkException
161 | */
162 | public HttpResponse post(String url, List params) throws OAuthSdkException {
163 | return this.post(url, params, null);
164 | }
165 |
166 | /**
167 | * post request with params and headers
168 | *
169 | * @param url
170 | * @param params
171 | * @param headers
172 | * @return
173 | * @throws OAuthSdkException
174 | */
175 | public HttpResponse post(String url, List params, Header[] headers) throws OAuthSdkException {
176 | try {
177 | URIBuilder builder = new URIBuilder(url);
178 | if (CollectionUtils.isNotEmpty(params)) {
179 | builder.setParameters(params);
180 | }
181 | HttpUriRequest request = new HttpPost(builder.build());
182 | if (ArrayUtils.isNotEmpty(headers)) {
183 | request.setHeaders(headers);
184 | }
185 | return httpClient.execute(request);
186 | } catch (URISyntaxException e) {
187 | log.error("The uri[{}] is illegal!", url, e);
188 | throw new OAuthSdkException("url syntax exception", e);
189 | } catch (IOException e) {
190 | log.error("Execute post request url[{}] error!", url, e);
191 | throw new OAuthSdkException("execute post request error", e);
192 | }
193 | }
194 |
195 | }
196 |
--------------------------------------------------------------------------------
/src/main/java/com/xiaomi/passport/common/ResponseType.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2017 xiaomi.com, Inc. All Rights Reserved
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.xiaomi.passport.common;
18 |
19 | /**
20 | * oauth 2.0 response type
21 | *
22 | * @author zhenchao.wang 2017-04-14 18:17:38
23 | * @version 1.0.0
24 | */
25 | public enum ResponseType {
26 |
27 | AUTHORIZATION_CODE("code"),
28 |
29 | IMPLICIT("token");
30 |
31 | private String type;
32 |
33 | ResponseType(String type) {
34 | this.type = type;
35 | }
36 |
37 | public String getType() {
38 | return type;
39 | }
40 |
41 | /**
42 | * tell whether {@param responseType} is valid
43 | *
44 | * @param responseType
45 | * @return
46 | */
47 | public static boolean isValid(String responseType) {
48 | return AUTHORIZATION_CODE.getType().equals(responseType) || IMPLICIT.getType().equals(responseType);
49 | }
50 |
51 | /**
52 | * change string param to {@code ResponseType} object
53 | *
54 | * @param responseType
55 | * @return
56 | */
57 | public static ResponseType parseToResponseType(String responseType) {
58 | for (final ResponseType type : ResponseType.values()) {
59 | if (type.getType().equals(responseType)) {
60 | return type;
61 | }
62 | }
63 | return null;
64 | }
65 |
66 | }
--------------------------------------------------------------------------------
/src/main/java/com/xiaomi/passport/constant/GlobalConstants.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2017 xiaomi.com, Inc. All Rights Reserved
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.xiaomi.passport.constant;
18 |
19 | /**
20 | * global constant
21 | *
22 | * @author zhenchao.wang 2017-04-14 13:46
23 | * @version 1.0.0
24 | */
25 | public interface GlobalConstants {
26 |
27 | /** oauth authorize request path */
28 |
29 | String OAUTH2_HOST = "https://account.xiaomi.com";
30 |
31 | String AUTHORIZE_PATH = "/oauth2/authorize";
32 |
33 | String TOKEN_PATH = "/oauth2/token";
34 |
35 | /** open api request path */
36 |
37 | String OPEN_API_HOST = "https://open.account.xiaomi.com";
38 |
39 | String USER_PROFILE_PATH = "/user/profile";
40 |
41 | String OPEN_ID_PATH = "/user/openidV2";
42 |
43 | String PHONE_AND_EMAIL_PATH = "/user/phoneAndEmail";
44 |
45 | String USER_REALTION_PATH = "/user/relation";
46 |
47 | String CHECK_PASSWORD_PATH = "/checkPassword";
48 |
49 | /** parameter name */
50 |
51 | String CLIENT_ID = "client_id";
52 |
53 | String TOKEN_TYPE = "token_type";
54 |
55 | String SCOPE = "scope";
56 |
57 | String STATE = "state";
58 |
59 | String ACCESS_TOKEN = "access_token";
60 |
61 | String MAC_KEY = "mac_key";
62 |
63 | String MAC_ALGORITHM = "mac_algorithm";
64 |
65 | String EXPIRES_IN = "expires_in";
66 |
67 | String REFRESH_TOKEN = "refresh_token";
68 |
69 | String RESPONSE_TYPE = "response_type";
70 |
71 | String REDIRECT_URI = "redirect_uri";
72 |
73 | String GRANT_TYPE = "grant_type";
74 |
75 | String CLIENT_SECRET = "client_secret";
76 |
77 | String CODE = "code";
78 |
79 | String SKIP_CONFIRM = "skip_confirm";
80 |
81 | String OPEN_ID = "openId";
82 |
83 | /** others */
84 |
85 | String HMAC_SHA1 = "HmacSHA1";
86 |
87 | String DEFAULT_CHARSET = "UTF-8";
88 |
89 | String JSON_SAFE_FLAG = "&&&START&&&";
90 |
91 | }
92 |
--------------------------------------------------------------------------------
/src/main/java/com/xiaomi/passport/exception/OAuthSdkException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2017 xiaomi.com, Inc. All Rights Reserved
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.xiaomi.passport.exception;
17 |
18 | import org.apache.commons.lang3.StringUtils;
19 |
20 | /**
21 | * user define exception
22 | *
23 | * @author zhenchao.wang 2017-04-14 13:53:00
24 | * @version 1.0.0
25 | */
26 | public class OAuthSdkException extends Exception {
27 |
28 | private static final long serialVersionUID = 1675452787643768680L;
29 |
30 | private int httpStatus = -1;
31 |
32 | private int errorCode = -1;
33 |
34 | /** error description */
35 | private String desc = StringUtils.EMPTY;
36 |
37 | public OAuthSdkException() {
38 | super();
39 | }
40 |
41 | public OAuthSdkException(String message) {
42 | super(message);
43 | }
44 |
45 | public OAuthSdkException(String message, Throwable cause) {
46 | super(message, cause);
47 | }
48 |
49 | public OAuthSdkException(Throwable cause) {
50 | super(cause);
51 | }
52 |
53 | public OAuthSdkException(int httpStatus) {
54 | this.httpStatus = httpStatus;
55 | }
56 |
57 | public OAuthSdkException(String message, int httpStatus) {
58 | super(message);
59 | this.httpStatus = httpStatus;
60 | }
61 |
62 | public OAuthSdkException(String message, Throwable cause, int httpStatus) {
63 | super(message, cause);
64 | this.httpStatus = httpStatus;
65 | }
66 |
67 | public OAuthSdkException(Throwable cause, int httpStatus) {
68 | super(cause);
69 | this.httpStatus = httpStatus;
70 | }
71 |
72 | public OAuthSdkException(int errorCode, String desc) {
73 | this.errorCode = errorCode;
74 | this.desc = desc;
75 | }
76 |
77 | public OAuthSdkException(String message, int errorCode, String desc) {
78 | super(message);
79 | this.errorCode = errorCode;
80 | this.desc = desc;
81 | }
82 |
83 | public OAuthSdkException(String message, Throwable cause, int errorCode, String desc) {
84 | super(message, cause);
85 | this.errorCode = errorCode;
86 | this.desc = desc;
87 | }
88 |
89 | public OAuthSdkException(Throwable cause, int errorCode, String desc) {
90 | super(cause);
91 | this.errorCode = errorCode;
92 | this.desc = desc;
93 | }
94 |
95 | public OAuthSdkException(int errorCode, int httpStatus, String desc) {
96 | this.errorCode = errorCode;
97 | this.httpStatus = httpStatus;
98 | this.desc = desc;
99 | }
100 |
101 | public OAuthSdkException(String message, int errorCode, int httpStatus, String desc) {
102 | super(message);
103 | this.errorCode = errorCode;
104 | this.httpStatus = httpStatus;
105 | this.desc = desc;
106 | }
107 |
108 | public OAuthSdkException(String message, Throwable cause, int errorCode, int httpStatus, String desc) {
109 | super(message, cause);
110 | this.errorCode = errorCode;
111 | this.httpStatus = httpStatus;
112 | this.desc = desc;
113 | }
114 |
115 | public OAuthSdkException(Throwable cause, int errorCode, int httpStatus, String desc) {
116 | super(cause);
117 | this.errorCode = errorCode;
118 | this.httpStatus = httpStatus;
119 | this.desc = desc;
120 | }
121 |
122 | public int getHttpStatus() {
123 | return httpStatus;
124 | }
125 |
126 | public int getErrorCode() {
127 | return errorCode;
128 | }
129 |
130 | public String getDesc() {
131 | return desc;
132 | }
133 | }
134 |
--------------------------------------------------------------------------------
/src/main/java/com/xiaomi/passport/pojo/AccessToken.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2017 xiaomi.com, Inc. All Rights Reserved
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.xiaomi.passport.pojo;
18 |
19 | import com.xiaomi.passport.constant.GlobalConstants;
20 | import net.sf.json.JSONObject;
21 | import org.apache.commons.lang3.StringUtils;
22 | import org.apache.commons.lang3.builder.ToStringBuilder;
23 | import org.apache.commons.lang3.builder.ToStringStyle;
24 |
25 | /**
26 | * access token
27 | *
28 | * @author zhenchao.wang 2017-04-14 14:46:38
29 | * @version 1.0.0
30 | */
31 | public class AccessToken {
32 |
33 | public enum TokenType {
34 |
35 | MAC("mac"),
36 |
37 | BEARER("bearer");
38 |
39 | private String type;
40 |
41 | TokenType(String type) {
42 | this.type = type;
43 | }
44 |
45 | public String getType() {
46 | return type;
47 | }
48 |
49 | /**
50 | * tell whether {@param tokenType} is valid
51 | *
52 | * @param tokenType
53 | * @return
54 | */
55 | public static boolean isValid(TokenType tokenType) {
56 | return MAC.equals(tokenType) || BEARER.equals(tokenType);
57 | }
58 | }
59 |
60 | /** access token */
61 | private String token;
62 |
63 | private String refreshToken;
64 |
65 | private String scope;
66 |
67 | private Long expiresIn;
68 |
69 | private String tokenType;
70 |
71 | private String macKey;
72 |
73 | private String macAlgorithm;
74 |
75 | private String openId;
76 |
77 | public AccessToken(JSONObject json) {
78 | this.tokenType = json.getString(GlobalConstants.TOKEN_TYPE);
79 | this.scope = json.getString(GlobalConstants.SCOPE);
80 | this.token = json.getString(GlobalConstants.ACCESS_TOKEN);
81 | if (TokenType.MAC.getType().equals(tokenType)) {
82 | this.macKey = json.getString(GlobalConstants.MAC_KEY);
83 | this.macAlgorithm = json.getString(GlobalConstants.MAC_ALGORITHM);
84 | }
85 | this.expiresIn = json.getLong(GlobalConstants.EXPIRES_IN);
86 | this.refreshToken = json.getString(GlobalConstants.REFRESH_TOKEN);
87 | this.openId = json.optString(GlobalConstants.OPEN_ID, StringUtils.EMPTY);
88 | }
89 |
90 | @Override
91 | public String toString() {
92 | return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
93 | }
94 |
95 | public String getToken() {
96 | return token;
97 | }
98 |
99 | public AccessToken setToken(String token) {
100 | this.token = token;
101 | return this;
102 | }
103 |
104 | public String getRefreshToken() {
105 | return refreshToken;
106 | }
107 |
108 | public AccessToken setRefreshToken(String refreshToken) {
109 | this.refreshToken = refreshToken;
110 | return this;
111 | }
112 |
113 | public String getScope() {
114 | return scope;
115 | }
116 |
117 | public AccessToken setScope(String scope) {
118 | this.scope = scope;
119 | return this;
120 | }
121 |
122 | public Long getExpiresIn() {
123 | return expiresIn;
124 | }
125 |
126 | public AccessToken setExpiresIn(Long expiresIn) {
127 | this.expiresIn = expiresIn;
128 | return this;
129 | }
130 |
131 | public String getTokenType() {
132 | return tokenType;
133 | }
134 |
135 | public AccessToken setTokenType(String tokenType) {
136 | this.tokenType = tokenType;
137 | return this;
138 | }
139 |
140 | public String getMacKey() {
141 | return macKey;
142 | }
143 |
144 | public AccessToken setMacKey(String macKey) {
145 | this.macKey = macKey;
146 | return this;
147 | }
148 |
149 | public String getMacAlgorithm() {
150 | return macAlgorithm;
151 | }
152 |
153 | public AccessToken setMacAlgorithm(String macAlgorithm) {
154 | this.macAlgorithm = macAlgorithm;
155 | return this;
156 | }
157 |
158 | public String getOpenId() {
159 | return openId;
160 | }
161 |
162 | public AccessToken setOpenId(String openId) {
163 | this.openId = openId;
164 | return this;
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/src/main/java/com/xiaomi/passport/pojo/Client.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2017 xiaomi.com, Inc. All Rights Reserved
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.xiaomi.passport.pojo;
18 |
19 | import org.apache.commons.lang3.builder.ToStringBuilder;
20 | import org.apache.commons.lang3.builder.ToStringStyle;
21 |
22 | /**
23 | * client pojo
24 | *
25 | * @author zhenchao.wang 2017-04-20 16:35
26 | * @version 1.0.0
27 | */
28 | public class Client {
29 |
30 | private Long id;
31 |
32 | private String name;
33 |
34 | private String key;
35 |
36 | private String secret;
37 |
38 | private String redirectUri;
39 |
40 | private Integer[] scopes;
41 |
42 | private String packageName;
43 |
44 | public Client() {
45 | }
46 |
47 | @Override
48 | public String toString() {
49 | return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
50 | }
51 |
52 | public Long getId() {
53 | return id;
54 | }
55 |
56 | public Client setId(Long id) {
57 | this.id = id;
58 | return this;
59 | }
60 |
61 | public String getName() {
62 | return name;
63 | }
64 |
65 | public Client setName(String name) {
66 | this.name = name;
67 | return this;
68 | }
69 |
70 | public String getKey() {
71 | return key;
72 | }
73 |
74 | public Client setKey(String key) {
75 | this.key = key;
76 | return this;
77 | }
78 |
79 | public String getSecret() {
80 | return secret;
81 | }
82 |
83 | public Client setSecret(String secret) {
84 | this.secret = secret;
85 | return this;
86 | }
87 |
88 | public String getRedirectUri() {
89 | return redirectUri;
90 | }
91 |
92 | public Client setRedirectUri(String redirectUri) {
93 | this.redirectUri = redirectUri;
94 | return this;
95 | }
96 |
97 | public Integer[] getScopes() {
98 | return scopes;
99 | }
100 |
101 | public Client setScopes(Integer[] scopes) {
102 | this.scopes = scopes;
103 | return this;
104 | }
105 |
106 | public String getPackageName() {
107 | return packageName;
108 | }
109 |
110 | public Client setPackageName(String packageName) {
111 | this.packageName = packageName;
112 | return this;
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/src/main/java/com/xiaomi/passport/pojo/UserProfile.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2017 xiaomi.com, Inc. All Rights Reserved
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.xiaomi.passport.pojo;
18 |
19 | import net.sf.json.JSONObject;
20 | import org.apache.commons.lang3.StringUtils;
21 | import org.apache.commons.lang3.builder.ToStringBuilder;
22 | import org.apache.commons.lang3.builder.ToStringStyle;
23 |
24 | import java.util.ArrayList;
25 | import java.util.List;
26 |
27 | /**
28 | * xiaomi user profile
29 | *
30 | * @author zhenchao.wang 2017-04-20 18:29
31 | * @version 1.0.0
32 | */
33 | public class UserProfile {
34 |
35 | private Long userId;
36 |
37 | private String openId;
38 |
39 | private String nickname;
40 |
41 | private String avatarUrl;
42 |
43 | private String phoneNumber;
44 |
45 | private String email;
46 |
47 | private List friends = new ArrayList();
48 |
49 | public UserProfile() {
50 | }
51 |
52 | public UserProfile(JSONObject json) {
53 | this.userId = json.optLong("userId", -1L);
54 | this.nickname = json.optString("miliaoNick", StringUtils.EMPTY);
55 | this.avatarUrl = json.optString("miliaoIcon", StringUtils.EMPTY);
56 | this.openId = json.optString("openid", StringUtils.EMPTY);
57 | this.phoneNumber = json.optString("phone", StringUtils.EMPTY);
58 | this.email = json.optString("email", StringUtils.EMPTY);
59 | String friends = json.optString("friends", StringUtils.EMPTY);
60 | if (StringUtils.isNotBlank(friends)) {
61 | List ids = new ArrayList();
62 | String[] elements = friends.split(",");
63 | for (final String element : elements) {
64 | ids.add(Long.valueOf(element));
65 | }
66 | this.friends = ids;
67 | }
68 | }
69 |
70 | @Override
71 | public String toString() {
72 | return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
73 | }
74 |
75 | public Long getUserId() {
76 | return userId;
77 | }
78 |
79 | public UserProfile setUserId(Long userId) {
80 | this.userId = userId;
81 | return this;
82 | }
83 |
84 | public String getOpenId() {
85 | return openId;
86 | }
87 |
88 | public UserProfile setOpenId(String openId) {
89 | this.openId = openId;
90 | return this;
91 | }
92 |
93 | public String getNickname() {
94 | return nickname;
95 | }
96 |
97 | public UserProfile setNickname(String nickname) {
98 | this.nickname = nickname;
99 | return this;
100 | }
101 |
102 | public String getAvatarUrl() {
103 | return avatarUrl;
104 | }
105 |
106 | public UserProfile setAvatarUrl(String avatarUrl) {
107 | this.avatarUrl = avatarUrl;
108 | return this;
109 | }
110 |
111 | public String getPhoneNumber() {
112 | return phoneNumber;
113 | }
114 |
115 | public UserProfile setPhoneNumber(String phoneNumber) {
116 | this.phoneNumber = phoneNumber;
117 | return this;
118 | }
119 |
120 | public String getEmail() {
121 | return email;
122 | }
123 |
124 | public UserProfile setEmail(String email) {
125 | this.email = email;
126 | return this;
127 | }
128 |
129 | public List getFriends() {
130 | return friends;
131 | }
132 |
133 | public UserProfile setFriends(List friends) {
134 | this.friends = friends;
135 | return this;
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/src/main/java/com/xiaomi/passport/util/AuthorizeUrlUtils.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2017 xiaomi.com, Inc. All Rights Reserved
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.xiaomi.passport.util;
18 |
19 | import com.xiaomi.passport.common.ResponseType;
20 | import com.xiaomi.passport.constant.GlobalConstants;
21 | import org.apache.commons.lang3.StringUtils;
22 | import org.apache.http.NameValuePair;
23 | import org.apache.http.client.utils.URLEncodedUtils;
24 | import org.apache.http.message.BasicNameValuePair;
25 |
26 | import java.util.ArrayList;
27 | import java.util.List;
28 |
29 | /**
30 | * authorize url util
31 | *
32 | * @author zhenchao.wang 2017-04-14 19:00
33 | * @version 1.0.0
34 | */
35 | public class AuthorizeUrlUtils implements GlobalConstants {
36 |
37 | private AuthorizeUrlUtils() {
38 | }
39 |
40 | /**
41 | * get authorization code authorize url
42 | *
43 | * @param clientId
44 | * @param redirectUri
45 | * @return
46 | */
47 | public static String getCodeAuthorizeUrl(long clientId, String redirectUri) {
48 | return getAuthorizeUrl(ResponseType.AUTHORIZATION_CODE, clientId, redirectUri, null, null);
49 | }
50 |
51 | /**
52 | * get authorization code authorize url
53 | *
54 | * @param clientId
55 | * @param redirectUri
56 | * @param scope
57 | * @return
58 | */
59 | public static String getCodeAuthorizeUrl(long clientId, String redirectUri, String scope) {
60 | return getAuthorizeUrl(ResponseType.AUTHORIZATION_CODE, clientId, redirectUri, scope, null);
61 | }
62 |
63 | /**
64 | * get authorize url
65 | *
66 | * @param responseType code or token
67 | * @param clientId
68 | * @param redirectUri
69 | * @param scope
70 | * @param state
71 | * @return
72 | */
73 | public static String getAuthorizeUrl(
74 | ResponseType responseType, long clientId, String redirectUri, String scope, String state) {
75 | List params = new ArrayList();
76 | params.add(new BasicNameValuePair(CLIENT_ID, String.valueOf(clientId)));
77 | params.add(new BasicNameValuePair(RESPONSE_TYPE, responseType.getType()));
78 | if (StringUtils.isNotEmpty(scope)) {
79 | params.add(new BasicNameValuePair(SCOPE, scope));
80 | }
81 | if (StringUtils.isNotEmpty(state)) {
82 | params.add(new BasicNameValuePair(STATE, state));
83 | }
84 | params.add(new BasicNameValuePair(REDIRECT_URI, redirectUri));
85 | String query = URLEncodedUtils.format(params, GlobalConstants.DEFAULT_CHARSET);
86 | return String.format("%s?%s", getAuthorizeUrl(), query);
87 | }
88 |
89 | /**
90 | * get authorize endpoint url
91 | *
92 | * @return
93 | */
94 | public static String getAuthorizeUrl() {
95 | return OAUTH2_HOST + AUTHORIZE_PATH;
96 | }
97 |
98 | /**
99 | * get token endpoint url
100 | *
101 | * @return
102 | */
103 | public static String getTokenUrl() {
104 | return OAUTH2_HOST + TOKEN_PATH;
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/main/java/com/xiaomi/passport/util/CommonUtils.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2017 xiaomi.com, Inc. All Rights Reserved
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package com.xiaomi.passport.util;
17 |
18 | import com.sun.org.apache.xml.internal.security.utils.Base64;
19 | import com.xiaomi.passport.constant.GlobalConstants;
20 | import org.apache.commons.lang3.StringUtils;
21 | import org.apache.http.Header;
22 | import org.apache.http.NameValuePair;
23 | import org.apache.http.client.utils.URLEncodedUtils;
24 | import org.apache.http.message.BasicHeader;
25 | import org.slf4j.Logger;
26 | import org.slf4j.LoggerFactory;
27 |
28 | import java.io.UnsupportedEncodingException;
29 | import java.net.URLEncoder;
30 | import java.security.InvalidKeyException;
31 | import java.security.NoSuchAlgorithmException;
32 | import java.security.SecureRandom;
33 | import java.util.ArrayList;
34 | import java.util.Collections;
35 | import java.util.Comparator;
36 | import java.util.List;
37 | import java.util.Random;
38 | import java.util.Scanner;
39 | import javax.crypto.Mac;
40 | import javax.crypto.spec.SecretKeySpec;
41 |
42 | /**
43 | * sdk common utils
44 | *
45 | * @author zhenchao.wang 2017-04-14 14:41:54
46 | * @version 1.0.0
47 | */
48 | public class CommonUtils {
49 |
50 | private static final Logger log = LoggerFactory.getLogger(CommonUtils.class);
51 |
52 | private static Random random = new SecureRandom();
53 |
54 | private CommonUtils() {
55 | }
56 |
57 | /**
58 | * generate mac signature
59 | * format : nonce + \n + method(POST\GET) + \n + host + \n + uriPath + \n + qs + \n
60 | *
61 | * @param nonce random string
62 | * @param method POST or GET
63 | * @param uriPath request url path
64 | * @param qs query param
65 | * @param macAlgorithm signature algorithm, must be HmacSHA1
66 | * @return
67 | * @throws NoSuchAlgorithmException
68 | * @throws InvalidKeyException
69 | */
70 | public static String getMacAccessTokenSignature(
71 | String nonce, String method, String host, String uriPath, String qs, String macKey, String macAlgorithm)
72 | throws InvalidKeyException, NoSuchAlgorithmException {
73 |
74 | log.debug("Get mac access token signature: nonce[{}], method[{}], host[{}], uriPath[{}], qs[{}], macKey[{}], macAlgorithm[{}].",
75 | nonce, method, host, uriPath, qs, macKey, macAlgorithm);
76 |
77 | List elements = new ArrayList();
78 | elements.add(nonce);
79 | elements.add(method.toUpperCase());
80 | elements.add(host);
81 | elements.add(uriPath);
82 |
83 | StringBuilder builder = new StringBuilder();
84 | if (StringUtils.isNotBlank(qs)) {
85 | // qs may be blank
86 | List params = new ArrayList();
87 | URLEncodedUtils.parse(params, new Scanner(qs), GlobalConstants.DEFAULT_CHARSET);
88 | Collections.sort(params, new Comparator() {
89 | public int compare(NameValuePair p1, NameValuePair p2) {
90 | return p1.getName().compareTo(p2.getName());
91 | }
92 | });
93 | builder.append(URLEncodedUtils.format(params, GlobalConstants.DEFAULT_CHARSET));
94 | }
95 | elements.add(builder.toString());
96 |
97 | String sign = StringUtils.join(elements.toArray(), "\n");
98 | sign += "\n"; // don't forget this
99 | log.debug("The generate max sign is [{}]", sign);
100 |
101 | if (GlobalConstants.HMAC_SHA1.equalsIgnoreCase(macAlgorithm)) {
102 | try {
103 | return Base64.encode(
104 | encryptHmacSha1(
105 | sign.getBytes(GlobalConstants.DEFAULT_CHARSET), macKey.getBytes(GlobalConstants.DEFAULT_CHARSET)));
106 | } catch (UnsupportedEncodingException e) {
107 | // never happen
108 | }
109 | } else {
110 | throw new NoSuchAlgorithmException("Unsupported mac algorithm : " + macAlgorithm);
111 | }
112 | return StringUtils.EMPTY;
113 | }
114 |
115 | /**
116 | * build mac request header content
117 | *
118 | * @param accessTokenId
119 | * @param nonce
120 | * @param mac
121 | * @return
122 | */
123 | public static Header buildMacRequestHeader(String accessTokenId, String nonce, String mac) {
124 | String content = "MAC access_token=\"%s\", nonce=\"%s\",mac=\"%s\"";
125 | try {
126 | content = String.format(content,
127 | URLEncoder.encode(accessTokenId, GlobalConstants.DEFAULT_CHARSET),
128 | URLEncoder.encode(nonce, GlobalConstants.DEFAULT_CHARSET),
129 | URLEncoder.encode(mac, GlobalConstants.DEFAULT_CHARSET));
130 | } catch (UnsupportedEncodingException e) {
131 | // never happen
132 | }
133 | return new BasicHeader("Authorization", content);
134 | }
135 |
136 | /**
137 | * generate nonce
138 | *
139 | * @return
140 | */
141 | public static String generateNonce() {
142 | return String.format("%d:%d", random.nextLong(), System.currentTimeMillis() / (1000 * 60));
143 | }
144 |
145 | /**
146 | * encrypt by HmacSha1
147 | *
148 | * @param data
149 | * @param key
150 | * @return return null if encrypt failed
151 | * @throws InvalidKeyException
152 | */
153 | public static byte[] encryptHmacSha1(byte[] data, byte[] key) throws InvalidKeyException {
154 | try {
155 | Mac mac = Mac.getInstance(GlobalConstants.HMAC_SHA1);
156 | mac.init(new SecretKeySpec(key, GlobalConstants.HMAC_SHA1));
157 | mac.update(data);
158 | return mac.doFinal();
159 | } catch (NoSuchAlgorithmException e) {
160 | // never happen
161 | }
162 | return null;
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/src/main/java/com/xiaomi/passport/util/HttpResponseUtils.java:
--------------------------------------------------------------------------------
1 | package com.xiaomi.passport.util;
2 |
3 | import com.xiaomi.passport.constant.GlobalConstants;
4 | import org.apache.commons.io.IOUtils;
5 | import org.apache.commons.lang3.StringUtils;
6 | import org.apache.http.HttpResponse;
7 |
8 | import java.io.IOException;
9 |
10 | /**
11 | * http response utils
12 | *
13 | * @author zhenchao.wang 2017-04-20 16:18
14 | * @version 1.0.0
15 | */
16 | public class HttpResponseUtils {
17 |
18 | private HttpResponseUtils() {
19 | }
20 |
21 | /**
22 | * if status is 302, 303, or 307
23 | *
24 | * @param response
25 | * @return
26 | */
27 | public static boolean isRedirectStatusLine(HttpResponse response) {
28 | if (null == response) {
29 | return false;
30 | }
31 | int status = response.getStatusLine().getStatusCode();
32 | return 302 == status || 303 == status || 307 == status;
33 | }
34 |
35 | /**
36 | * get http response entity as string format
37 | *
38 | * @param response
39 | * @return empty string when no content or error content
40 | */
41 | public static String getEntityContent(HttpResponse response) {
42 | if (null == response || null == response.getEntity()) {
43 | return StringUtils.EMPTY;
44 | }
45 | try {
46 | return StringUtils.replace(IOUtils.toString(response.getEntity().getContent(),
47 | GlobalConstants.DEFAULT_CHARSET), GlobalConstants.JSON_SAFE_FLAG, StringUtils.EMPTY);
48 | } catch (IOException e) {
49 | // return empty string
50 | }
51 | return StringUtils.EMPTY;
52 | }
53 |
54 | }
55 |
--------------------------------------------------------------------------------
/src/main/resources/log4j.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/test/java/com/xiaomi/passport/api/AuthorizationCodeGrantHelperTest.java:
--------------------------------------------------------------------------------
1 | package com.xiaomi.passport.api;
2 |
3 | import com.xiaomi.passport.exception.OAuthSdkException;
4 | import com.xiaomi.passport.pojo.AccessToken;
5 | import com.xiaomi.passport.pojo.Client;
6 | import com.xiaomi.passport.utils.OAuthTestUtils;
7 | import org.apache.commons.lang3.RandomStringUtils;
8 | import org.apache.commons.lang3.StringUtils;
9 | import org.junit.Assert;
10 | import org.junit.Before;
11 | import org.junit.Ignore;
12 | import org.junit.Test;
13 |
14 | /**
15 | * @author zhenchao.wang 2017-04-20 16:30
16 | * @version 1.0.0
17 | */
18 | public class AuthorizationCodeGrantHelperTest {
19 |
20 | private Client client;
21 |
22 | private AuthorizationCodeGrantHelper helper;
23 |
24 | @Before
25 | public void setUp() throws Exception {
26 | client = OAuthTestUtils.getTestClient();
27 | helper = new AuthorizationCodeGrantHelper(client);
28 | }
29 |
30 | @Test
31 | @Ignore
32 | public void getAuthorizationCodeTest() throws Exception {
33 | helper.getAuthorizationCode(false, StringUtils.EMPTY);
34 | }
35 |
36 | @Test
37 | public void getAccessTokenByCodeWithErrorTest() throws Exception {
38 | String errorCode = RandomStringUtils.randomAlphabetic(32);
39 | try {
40 | helper.getAccessTokenByCode(errorCode);
41 | Assert.fail();
42 | } catch (OAuthSdkException e) {
43 | e.printStackTrace();
44 | Assert.assertEquals(96013, e.getErrorCode());
45 | }
46 | }
47 |
48 | @Test
49 | public void getAccessTokenByCodeTest() throws Exception {
50 | String code = "your authorization code here";
51 | try {
52 | AccessToken accessToken = helper.getAccessTokenByCode(code);
53 | Assert.assertNotNull(accessToken);
54 | System.out.println("access token : " + accessToken);
55 | Assert.assertTrue(StringUtils.isNotBlank(accessToken.getToken()));
56 | Assert.assertEquals(AccessToken.TokenType.MAC.getType(), accessToken.getTokenType());
57 | Assert.assertTrue(StringUtils.isNotBlank(accessToken.getOpenId()));
58 | Assert.assertTrue(StringUtils.isNotBlank(accessToken.getRefreshToken()));
59 | } catch (OAuthSdkException e) {
60 | Assert.fail();
61 | }
62 | }
63 |
64 | }
--------------------------------------------------------------------------------
/src/test/java/com/xiaomi/passport/api/ImplicitGrantHelperTest.java:
--------------------------------------------------------------------------------
1 | package com.xiaomi.passport.api;
2 |
3 | import com.xiaomi.passport.pojo.Client;
4 | import com.xiaomi.passport.utils.OAuthTestUtils;
5 | import org.apache.commons.lang3.StringUtils;
6 | import org.junit.Before;
7 | import org.junit.Ignore;
8 | import org.junit.Test;
9 |
10 | /**
11 | * @author zhenchao.wang 2017-04-21 11:42
12 | * @version 1.0.0
13 | */
14 | public class ImplicitGrantHelperTest {
15 |
16 | private Client client;
17 |
18 | private ImplicitGrantHelper helper;
19 |
20 | @Before
21 | public void setUp() throws Exception {
22 | client = OAuthTestUtils.getTestClient();
23 | helper = new ImplicitGrantHelper(client);
24 | }
25 |
26 | @Test
27 | @Ignore
28 | public void getImplicitAccessToken() throws Exception {
29 | helper.getImplicitAccessToken(false, StringUtils.EMPTY);
30 | }
31 |
32 | }
--------------------------------------------------------------------------------
/src/test/java/com/xiaomi/passport/api/OpenApiHelperTest.java:
--------------------------------------------------------------------------------
1 | package com.xiaomi.passport.api;
2 |
3 | import com.xiaomi.passport.pojo.Client;
4 | import com.xiaomi.passport.pojo.UserProfile;
5 | import com.xiaomi.passport.utils.OAuthTestUtils;
6 | import org.apache.commons.collections.CollectionUtils;
7 | import org.apache.commons.lang3.StringUtils;
8 | import org.apache.commons.lang3.tuple.Pair;
9 | import org.junit.Assert;
10 | import org.junit.Before;
11 | import org.junit.Test;
12 |
13 | import java.util.List;
14 |
15 | /**
16 | * @author zhenchao.wang 2017-04-21 11:44
17 | * @version 1.0.0
18 | */
19 | public class OpenApiHelperTest {
20 |
21 | private Client client;
22 |
23 | private String accessToken = "your access token here";
24 |
25 | private OpenApiHelper helper;
26 |
27 | @Before
28 | public void setUp() throws Exception {
29 | client = OAuthTestUtils.getTestClient();
30 | helper = new OpenApiHelper(client.getId(), accessToken);
31 | }
32 |
33 | @Test
34 | public void getUserProfile() throws Exception {
35 | UserProfile profile = helper.getUserProfile();
36 | Assert.assertNotNull(profile);
37 | Assert.assertTrue(profile.getUserId() > 0);
38 | Assert.assertTrue(StringUtils.isNotBlank(profile.getNickname()));
39 | Assert.assertTrue(StringUtils.isNotBlank(profile.getAvatarUrl()));
40 | }
41 |
42 | @Test
43 | public void getOpenId() throws Exception {
44 | Assert.assertTrue(StringUtils.isNotBlank(helper.getOpenId()));
45 | }
46 |
47 | @Test
48 | public void getPhoneAndEmail() throws Exception {
49 | Pair pair = helper.getPhoneAndEmail();
50 | Assert.assertNotNull(pair);
51 | Assert.assertTrue(StringUtils.isNotBlank(pair.getLeft()));
52 | Assert.assertTrue(StringUtils.isNotBlank(pair.getRight()));
53 | }
54 |
55 | @Test
56 | public void getFriendIdList() throws Exception {
57 | List friends = helper.getFriendIdList();
58 | Assert.assertTrue(CollectionUtils.isNotEmpty(friends));
59 | }
60 |
61 | }
--------------------------------------------------------------------------------
/src/test/java/com/xiaomi/passport/api/RefreshAccessTokenHelperTest.java:
--------------------------------------------------------------------------------
1 | package com.xiaomi.passport.api;
2 |
3 | import com.xiaomi.passport.exception.OAuthSdkException;
4 | import com.xiaomi.passport.pojo.AccessToken;
5 | import com.xiaomi.passport.pojo.Client;
6 | import com.xiaomi.passport.utils.OAuthTestUtils;
7 | import org.apache.commons.lang.RandomStringUtils;
8 | import org.apache.commons.lang3.StringUtils;
9 | import org.junit.Assert;
10 | import org.junit.Before;
11 | import org.junit.Test;
12 |
13 | /**
14 | * @author zhenchao.wang 2017-04-21 11:43
15 | * @version 1.0.0
16 | */
17 | public class RefreshAccessTokenHelperTest {
18 |
19 | private Client client;
20 |
21 | private RefreshAccessTokenHelper helper;
22 |
23 | @Before
24 | public void setUp() throws Exception {
25 | client = OAuthTestUtils.getTestClient();
26 | helper = new RefreshAccessTokenHelper(client);
27 | }
28 |
29 | @Test
30 | public void refreshAccessTokenWithErrorTest() throws Exception {
31 | String refreshToken = RandomStringUtils.randomAlphanumeric(32);
32 | try {
33 | helper.refreshAccessToken(refreshToken);
34 | Assert.fail();
35 | } catch (OAuthSdkException e) {
36 | Assert.assertEquals(96009, e.getErrorCode());
37 | }
38 | }
39 |
40 | @Test
41 | public void refreshAccessTokenTest() throws Exception {
42 | String refreshToken = "your refresh token here";
43 | try {
44 | AccessToken accessToken = helper.refreshAccessToken(refreshToken);
45 | Assert.assertNotNull(accessToken);
46 | System.out.println("access token : " + accessToken);
47 | Assert.assertTrue(StringUtils.isNotBlank(accessToken.getToken()));
48 | Assert.assertEquals(AccessToken.TokenType.MAC.getType(), accessToken.getTokenType());
49 | Assert.assertTrue(StringUtils.isNotBlank(accessToken.getOpenId()));
50 | Assert.assertTrue(StringUtils.isNotBlank(accessToken.getRefreshToken()));
51 | } catch (OAuthSdkException e) {
52 | e.printStackTrace();
53 | Assert.fail();
54 | }
55 | }
56 |
57 | }
--------------------------------------------------------------------------------
/src/test/java/com/xiaomi/passport/utils/OAuthTestUtils.java:
--------------------------------------------------------------------------------
1 | package com.xiaomi.passport.utils;
2 |
3 | import com.xiaomi.passport.pojo.Client;
4 |
5 | /**
6 | * @author zhenchao.wang 2017-04-20 16:39
7 | * @version 1.0.0
8 | */
9 | public class OAuthTestUtils {
10 |
11 | private OAuthTestUtils() {
12 | }
13 |
14 | public static Client getTestClient() {
15 | Client client = new Client();
16 | // initialize your app info
17 | return client;
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/test/resources/log4j.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------