如何集成三方 Oauth2 单点登录
如何集成
配置文件追加自定义的 oauth2 类型
添加自定义 oauth2 类型枚举
添加自定义 oauth2 类型相关地址信息
自定义 oauth2 实现,代码建参考
注入实现
前端登录页添加 oauth2 跳转图标
常见问题
- 如何直接通过 oauth2 url 进入到系统而不通过登录页面点击?
可以直接通过如下地址,记得在配置文件里面配置 ignore-check-state=true,忽略 state 的校验,http://ip:port/oauth/authorize?response_type=code&client_id=xxxxx&state=300ec11cb0cef12c23b8ba137fe7f51e&scope=all&redirect_uri=https://surveykingpro.cn/user/login
参考代码
package cn.surveyking.framework.social.core.custom;
import cn.hutool.core.codec.Base64;
import cn.hutool.http.Header;
import cn.hutool.http.HttpRequest;
import cn.surveyking.framework.social.core.enums.AuthExtendSource;
import com.alibaba.fastjson.JSONObject;
import me.zhyd.oauth.cache.AuthStateCache;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.enums.AuthUserGender;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthToken;
import me.zhyd.oauth.model.AuthUser;
import me.zhyd.oauth.request.AuthDefaultRequest;
import me.zhyd.oauth.utils.UrlBuilder;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author javahuang
* @date 2023/11/13
*/
public class AuthCustomRequest extends AuthDefaultRequest {
public AuthCustomRequest(AuthConfig config) {
super(config, AuthExtendSource.CUSTOM_SOURCE);
}
public AuthCustomRequest(AuthConfig config, AuthStateCache authStateCache) {
super(config, AuthExtendSource.CUSTOM_SOURCE, authStateCache);
}
@Override
protected AuthToken getAccessToken(AuthCallback authCallback) {
String tokenUrl = accessTokenUrl(authCallback.getCode());
Map<String, Object> paramMap = new LinkedHashMap<>();
String encode = Base64.encode(String.format("%s:%s", config.getClientId(), config.getClientSecret()));
paramMap.put("code", authCallback.getCode());
paramMap.put("client_id", config.getClientId());
paramMap.put("grant_type", "authorization_code");
paramMap.put("redirect_uri", config.getRedirectUri());
paramMap.put("scope", "all");
String response = HttpRequest.post(tokenUrl).header("Content-Type", "application/x-www-form-urlencoded")
.header(Header.AUTHORIZATION, String.format("Basic %s", encode))// 头信息,多个头信息多次调用此方法即可
.form(paramMap)// 表单内容
.timeout(20000)// 超时,毫秒
.execute().body();
JSONObject object = JSONObject.parseObject(response);
return AuthToken.builder().accessToken(object.getString("access_token"))
.refreshToken(object.getString("refresh_token")).idToken(object.getString("id_token"))
.tokenType(object.getString("token_type")).scope(object.getString("scope")).build();
}
@Override
protected AuthUser getUserInfo(AuthToken authToken) {
String response = doGetUserInfo(authToken);
JSONObject object = JSONObject.parseObject(response);
this.checkResponse(object);
return AuthUser.builder()
.uuid(object.getString("id"))
.username(object.getString("username"))
.nickname(object.getString("name"))
.avatar(object.getString("avatar_url"))
.blog(object.getString("web_url"))
.company(object.getString("organization"))
.location(object.getString("location"))
.email(object.getString("email"))
.remark(object.getString("bio"))
.gender(AuthUserGender.UNKNOWN)
.token(authToken)
.source(source.toString())
.build();
}
private void checkResponse(JSONObject object) {
// oauth/token 验证异常
if (object.containsKey("error")) {
throw new AuthException(object.getString("error_description"));
}
// user 验证异常
if (object.containsKey("message")) {
throw new AuthException(object.getString("message"));
}
}
/**
* 返回带{@code state}参数的授权url,授权回调时会带上这个{@code state}
*
* @param state state 验证授权流程的参数,可以防止csrf
* @return 返回授权地址
* @since 1.11.0
*/
@Override
public String authorize(String state) {
return UrlBuilder.fromBaseUrl(super.authorize(state)).queryParam("scope", "all").build();
}
}