第三方登录集成
OAuth2 统一接入模式
用户 → 选择第三方登录
│
├─ 跳转第三方 OAuth 授权页
│ ?response_type=code
│ &client_id=APP_ID
│ &redirect_uri=https://myapp/callback/{platform}
│ &state=随机值
│
├─ 用户授权 → 回调 code
│
├─ 后端用 code 换 access_token
│ POST /oauth/access_token
│ appid=APP_ID&secret=APP_SECRET&code=CODE
│
├─ 用 access_token 获取用户信息
│ GET /userinfo?access_token=xxx
│
└─ 注册 / 登录(绑定本地账号或自动注册)微信登录
开放平台流程
微信开放平台 → 网站应用 → AppID + AppSecretjava
public class WeChatLoginService {
private static final String TOKEN_URL =
"https://api.weixin.qq.com/sns/oauth2/access_token";
private static final String USERINFO_URL =
"https://api.weixin.qq.com/sns/userinfo";
public WeChatUser login(String code) {
// 1. 获取 access_token
String resp = HttpUtil.get(TOKEN_URL, Map.of(
"appid", appId,
"secret", appSecret,
"code", code,
"grant_type", "authorization_code"
));
JSONObject json = JSON.parseObject(resp);
String token = json.getString("access_token");
String openId = json.getString("openid");
// 2. 获取用户信息
String userResp = HttpUtil.get(USERINFO_URL, Map.of(
"access_token", token,
"openid", openId
));
return JSON.parseObject(userResp, WeChatUser.class);
}
}钉钉登录
扫码模式
java
public class DingTalkLoginService {
// 1. 构造扫码 URL(前端跳转)
public String getQrCodeUrl() {
return "https://login.dingtalk.com/oauth2/auth" +
"?redirect_uri=" + URLEncoder.encode(redirectUri) +
"&response_type=code" +
"&client_id=" + appId +
"&scope=openid" +
"&state=STATE";
}
// 2. 回调处理
public DingTalkUser login(String code) {
// 获取 access_token
JSONObject tokenResp = HttpUtil.post(
"https://api.dingtalk.com/v1.0/oauth2/userAccessToken",
Map.of("clientId", appId, "clientSecret", appSecret, "code", code)
);
String token = tokenResp.getString("accessToken");
// 获取用户信息
JSONObject userResp = HttpUtil.get(
"https://api.dingtalk.com/v1.0/contact/users/me",
Map.of("x-acs-dingtalk-access-token", token)
);
return userResp.toJavaObject(DingTalkUser.class);
}
}企业微信登录
java
public class WeComLoginService {
// 构造授权 URL
public String getAuthUrl() {
return "https://open.weixin.qq.com/connect/oauth2/authorize" +
"?appid=" + corpId +
"&redirect_uri=" + URLEncoder.encode(redirectUri) +
"&response_type=code" +
"&scope=snsapi_privateinfo" +
"&state=STATE#wechat_redirect";
}
// 获取用户信息
public WeComUser login(String code) {
// 获取 access_token(企业微信需先获取 corp access token)
JSONObject tokenResp = HttpUtil.get(
"https://qyapi.weixin.qq.com/cgi-bin/gettoken",
Map.of("corpid", corpId, "corpsecret", corpSecret)
);
String token = tokenResp.getString("access_token");
// 获取用户详情
JSONObject userResp = HttpUtil.post(
"https://qyapi.weixin.qq.com/cgi-bin/auth/getuserdetail",
Map.of("access_token", token),
Map.of("user_ticket", code)
);
return userResp.toJavaObject(WeComUser.class);
}
}飞书登录
java
public class FeishuLoginService {
public FeishuUser login(String code) {
// 获取 access_token
JSONObject tokenResp = HttpUtil.post(
"https://open.feishu.cn/open-apis/authen/v1/access_token",
Map.of(
"app_id", appId,
"app_secret", appSecret,
"grant_type", "authorization_code",
"code", code
)
);
String token = tokenResp.getJSONObject("data").getString("access_token");
// 获取用户信息
JSONObject userResp = HttpUtil.get(
"https://open.feishu.cn/open-apis/authen/v1/user_info",
Map.of("Authorization", "Bearer " + token)
);
return userResp.getJSONObject("data").toJavaObject(FeishuUser.class);
}
}统一登录网关设计
策略模式解耦
java
public interface OAuthLoginHandler {
String getPlatform();
OAuthUser login(String code);
}
@Component
public class OAuthLoginGateway {
private final Map<String, OAuthLoginHandler> handlerMap;
public OAuthLoginGateway(List<OAuthLoginHandler> handlers) {
handlerMap = handlers.stream()
.collect(Collectors.toMap(OAuthLoginHandler::getPlatform, Function.identity()));
}
public OAuthUser login(String platform, String code) {
OAuthLoginHandler handler = handlerMap.get(platform);
if (handler == null) {
throw new UnsupportedOperationException("不支持的登录平台: " + platform);
}
return handler.login(code);
}
}