package me.zhyd.oauth.request; import com.alibaba.fastjson.JSONObject; import com.xkcoding.http.HttpUtil; import com.xkcoding.http.constants.Constants; import com.xkcoding.http.support.HttpHeader; import com.xkcoding.http.util.MapUtil; import me.zhyd.oauth.cache.AuthStateCache; import me.zhyd.oauth.config.AuthConfig; import me.zhyd.oauth.model.AuthCallback; import me.zhyd.oauth.model.AuthToken; import me.zhyd.oauth.model.AuthUser; import me.zhyd.oauth.utils.GlobalAuthUtils; import me.zhyd.oauth.utils.UrlBuilder; import java.util.HashMap; import java.util.Map; import static me.zhyd.oauth.config.AuthDefaultSource.TWITTER; import static me.zhyd.oauth.utils.GlobalAuthUtils.generateTwitterSignature; import static me.zhyd.oauth.utils.GlobalAuthUtils.urlEncode; /** * Twitter登录 * * @author hongwei.peng (pengisgood(at)gmail(dot)com) * @since 1.13.0 */ public class AuthTwitterRequest extends AuthDefaultRequest { private static final String PREAMBLE = "OAuth"; public AuthTwitterRequest(AuthConfig config) { super(config, TWITTER); } public AuthTwitterRequest(AuthConfig config, AuthStateCache authStateCache) { super(config, TWITTER, authStateCache); } /** * 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state} * * @param state state 验证授权流程的参数,可以防止csrf * @return 返回授权地址 * @since 1.9.3 */ @Override public String authorize(String state) { AuthToken token = this.getRequestToken(); return UrlBuilder.fromBaseUrl(source.authorize()) .queryParam("oauth_token", token.getOauthToken()) .build(); } /** * Obtaining a request token * https://developer.twitter.com/en/docs/twitter-for-websites/log-in-with-twitter/guides/implementing-sign-in-with-twitter * * @return request token */ public AuthToken getRequestToken() { String baseUrl = "https://api.twitter.com/oauth/request_token"; Map oauthParams = buildOauthParams(); oauthParams.put("oauth_callback", config.getRedirectUri()); oauthParams.put("oauth_signature", generateTwitterSignature(oauthParams, "POST", baseUrl, config.getClientSecret(), null)); String header = buildHeader(oauthParams); HttpHeader httpHeader = new HttpHeader(); httpHeader.add("Authorization", header); httpHeader.add("User-Agent", "themattharris' HTTP Client"); httpHeader.add("Host", "api.twitter.com"); httpHeader.add("Accept", "*/*"); String requestToken = HttpUtil.post(baseUrl, null, httpHeader); Map res = MapUtil.parseStringToMap(requestToken, false); return AuthToken.builder() .oauthToken(res.get("oauth_token")) .oauthTokenSecret(res.get("oauth_token_secret")) .oauthCallbackConfirmed(Boolean.valueOf(res.get("oauth_callback_confirmed"))) .build(); } /** * Convert request token to access token * https://developer.twitter.com/en/docs/twitter-for-websites/log-in-with-twitter/guides/implementing-sign-in-with-twitter * * @return access token */ @Override protected AuthToken getAccessToken(AuthCallback authCallback) { Map oauthParams = buildOauthParams(); oauthParams.put("oauth_token", authCallback.getOauth_token()); oauthParams.put("oauth_verifier", authCallback.getOauth_verifier()); oauthParams.put("oauth_signature", generateTwitterSignature(oauthParams, "POST", source.accessToken(), config.getClientSecret(), authCallback .getOauth_token())); String header = buildHeader(oauthParams); HttpHeader httpHeader = new HttpHeader(); httpHeader.add("Authorization", header); httpHeader.add(Constants.CONTENT_TYPE, "application/x-www-form-urlencoded"); Map form = new HashMap<>(1); form.put("oauth_verifier", authCallback.getOauth_verifier()); String response = HttpUtil.post(source.accessToken(), form, httpHeader, false); Map requestToken = MapUtil.parseStringToMap(response, false); return AuthToken.builder() .oauthToken(requestToken.get("oauth_token")) .oauthTokenSecret(requestToken.get("oauth_token_secret")) .userId(requestToken.get("user_id")) .screenName(requestToken.get("screen_name")) .build(); } @Override protected AuthUser getUserInfo(AuthToken authToken) { Map queryParams = new HashMap<>(); queryParams.put("user_id", authToken.getUserId()); queryParams.put("screen_name", authToken.getScreenName()); queryParams.put("include_entities", Boolean.toString(true)); Map oauthParams = buildOauthParams(); oauthParams.put("oauth_token", authToken.getOauthToken()); Map params = new HashMap<>(oauthParams); params.putAll(queryParams); oauthParams.put("oauth_signature", generateTwitterSignature(params, "GET", source.userInfo(), config.getClientSecret(), authToken .getOauthTokenSecret())); String header = buildHeader(oauthParams); HttpHeader httpHeader = new HttpHeader(); httpHeader.add("Authorization", header); String response = HttpUtil.get(userInfoUrl(authToken), null, httpHeader, false); JSONObject userInfo = JSONObject.parseObject(response); return AuthUser.builder() .uuid(userInfo.getString("id_str")) .username(userInfo.getString("screen_name")) .nickname(userInfo.getString("name")) .remark(userInfo.getString("description")) .avatar(userInfo.getString("profile_image_url_https")) .blog(userInfo.getString("url")) .location(userInfo.getString("location")) .avatar(userInfo.getString("profile_image_url")) .source(source.toString()) .token(authToken) .build(); } @Override protected String userInfoUrl(AuthToken authToken) { return UrlBuilder.fromBaseUrl(source.userInfo()) .queryParam("user_id", authToken.getUserId()) .queryParam("screen_name", authToken.getScreenName()) .queryParam("include_entities", true) .build(); } private Map buildOauthParams() { Map params = new HashMap<>(5); params.put("oauth_consumer_key", config.getClientId()); params.put("oauth_nonce", GlobalAuthUtils.generateNonce(32)); params.put("oauth_signature_method", "HMAC-SHA1"); params.put("oauth_timestamp", GlobalAuthUtils.getTimestamp()); params.put("oauth_version", "1.0"); return params; } private String buildHeader(Map oauthParams) { final StringBuilder sb = new StringBuilder(PREAMBLE + " "); for (Map.Entry param : oauthParams.entrySet()) { sb.append(param.getKey()).append("=\"").append(urlEncode(param.getValue())).append('"').append(", "); } return sb.deleteCharAt(sb.length() - 2).toString(); } }