✨ 添加新特性。 支持开源中国、码云登录
This commit is contained in:
1 parent
f6094954b6
commit
5615b78c5e
13 files changed
+352
-9
No files matched your search
@@ -87,6 +87,7 @@ public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdap
|
||||
.userDetailsService(pigxUserDetailsService)
|
||||
.authenticationManager(authenticationManagerBean)
|
||||
.reuseRefreshTokens(false)
|
||||
.pathMapping("/oauth/confirm_access", "/token/confirm_access")
|
||||
.exceptionTranslator(new PigxWebResponseExceptionTranslator());
|
||||
}
|
||||
|
||||
|
||||
@@ -39,11 +39,16 @@ import org.springframework.data.redis.serializer.RedisSerializer;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.security.oauth2.common.OAuth2AccessToken;
|
||||
import org.springframework.security.oauth2.provider.AuthorizationRequest;
|
||||
import org.springframework.security.oauth2.provider.ClientDetails;
|
||||
import org.springframework.security.oauth2.provider.ClientDetailsService;
|
||||
import org.springframework.security.oauth2.provider.OAuth2Authentication;
|
||||
import org.springframework.security.oauth2.provider.token.TokenStore;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.servlet.ModelAndView;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -58,6 +63,7 @@ import java.util.Map;
|
||||
@RequestMapping("/token")
|
||||
public class PigxTokenEndpoint {
|
||||
private static final String PIGX_OAUTH_ACCESS = SecurityConstants.PIGX_PREFIX + SecurityConstants.OAUTH_PREFIX + "auth_to_access:";
|
||||
private final ClientDetailsService clientDetailsService;
|
||||
private final RedisTemplate pigxRedisTemplate;
|
||||
private final TokenStore tokenStore;
|
||||
private final CacheManager cacheManager;
|
||||
@@ -65,11 +71,37 @@ public class PigxTokenEndpoint {
|
||||
/**
|
||||
* 认证页面
|
||||
*
|
||||
* @param modelAndView
|
||||
* @return ModelAndView
|
||||
*/
|
||||
@GetMapping("/login")
|
||||
public ModelAndView require() {
|
||||
return new ModelAndView("ftl/login");
|
||||
public ModelAndView require(ModelAndView modelAndView) {
|
||||
modelAndView.setViewName("ftl/login");
|
||||
return modelAndView;
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认授权页面
|
||||
*
|
||||
* @param request
|
||||
* @param session
|
||||
* @param modelAndView
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/confirm_access")
|
||||
public ModelAndView confirm(HttpServletRequest request, HttpSession session, ModelAndView modelAndView) {
|
||||
Map<String, Object> scopeList = (Map<String, Object>) request.getAttribute("scopes");
|
||||
modelAndView.addObject("scopeList", scopeList.keySet());
|
||||
|
||||
Object auth = session.getAttribute("authorizationRequest");
|
||||
if (auth != null) {
|
||||
AuthorizationRequest authorizationRequest = (AuthorizationRequest) auth;
|
||||
ClientDetails clientDetails = clientDetailsService.loadClientByClientId(authorizationRequest.getClientId());
|
||||
modelAndView.addObject("app", clientDetails.getAdditionalInformation());
|
||||
}
|
||||
|
||||
modelAndView.setViewName("ftl/confirm");
|
||||
return modelAndView;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
body {
|
||||
.sign_body {
|
||||
padding-top: 40px;
|
||||
padding-bottom: 40px;
|
||||
background-color: #eee;
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport"
|
||||
content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"/>
|
||||
<title>PigX第三方授权</title>
|
||||
<link rel="stylesheet" type="text/css" href="/css/bootstrap.min.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="/css/signin.css"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<nav class="navbar navbar-default container-fluid">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<a class="navbar-brand" href="#">开放平台</a>
|
||||
</div>
|
||||
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-5">
|
||||
<p class="navbar-text navbar-right">
|
||||
<a target="_blank" href="https://pig4cloud.com">技术支持</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div style="padding-top: 80px;width: 300px; color: #555; margin:0px auto;">
|
||||
<form id='confirmationForm' name='confirmationForm' action="/oauth/authorize" method='post'>
|
||||
<input name='user_oauth_approval' value='true' type='hidden'/>
|
||||
<p><a href="${app.website}" target="_blank">${app.appName}</a> 将获得以下权限:</p>
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item"> <span>
|
||||
<#list scopeList as scope>
|
||||
<input type="hidden" name="${scope}" value="true"/>
|
||||
<input type="checkbox" disabled checked="checked"/><label>${scope}</label>
|
||||
</#list>
|
||||
</ul>
|
||||
<p class="help-block">授权后表明你已同意 <a>服务协议</a></p>
|
||||
<button class="btn btn-success pull-right" type="submit" id="write-email-btn">授权</button>
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
<footer>
|
||||
<p>support by: pig4cloud.com</p>
|
||||
<p>email: <a href="mailto:wangiegie@gmail.com">wangiegie@gmail.com</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
@@ -14,7 +14,7 @@
|
||||
<link href="/css/signin.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<body class="sign_body">
|
||||
<div class="container form-margin-top">
|
||||
<form class="form-signin" action="/token/form" method="post">
|
||||
<h2 class="form-signin-heading" align="center">统一认证系统</h2>
|
||||
@@ -24,7 +24,7 @@
|
||||
</form>
|
||||
</div>
|
||||
<footer>
|
||||
<p>support by: lengleng</p>
|
||||
<p>support by: pig4cloud</p>
|
||||
<p>email: <a href="mailto:wangiegie@gmail.com">wangiegie@gmail.com</a>.</p>
|
||||
</footer>
|
||||
</body>
|
||||
|
||||
+22
@@ -93,6 +93,28 @@ public interface SecurityConstants {
|
||||
String WX_AUTHORIZATION_CODE_URL = "https://api.weixin.qq.com/sns/oauth2/access_token" +
|
||||
"?appid=%s&secret=%s&code=%s&grant_type=authorization_code";
|
||||
|
||||
/**
|
||||
* 码云获取token
|
||||
*/
|
||||
String GITEE_AUTHORIZATION_CODE_URL = "https://gitee.com/oauth/token?grant_type=" +
|
||||
"authorization_code&code=%S&client_id=%s&redirect_uri=" +
|
||||
"%s&client_secret=%s";
|
||||
|
||||
/**
|
||||
* 开源中国获取token
|
||||
*/
|
||||
String OSC_AUTHORIZATION_CODE_URL = "https://www.oschina.net/action/openapi/token";
|
||||
|
||||
/**
|
||||
* 码云获取用户信息
|
||||
*/
|
||||
String GITEE_USER_INFO_URL = "https://gitee.com/api/v5/user?access_token=%s";
|
||||
|
||||
/**
|
||||
* 开源中国用户信息
|
||||
*/
|
||||
String OSC_USER_INFO_URL = "https://www.oschina.net/action/openapi/user?access_token=%s&dataType=json";
|
||||
|
||||
/**
|
||||
* {bcrypt} 加密的特征码
|
||||
*/
|
||||
|
||||
+11
-1
@@ -46,7 +46,17 @@ public enum LoginTypeEnum {
|
||||
/**
|
||||
* 微信登录
|
||||
*/
|
||||
WECHAT("WX", "微信登录");
|
||||
WECHAT("WX", "微信登录"),
|
||||
|
||||
/**
|
||||
* 码云登录
|
||||
*/
|
||||
GITEE("GITEE", "码云登录"),
|
||||
|
||||
/**
|
||||
* 开源中国登录
|
||||
*/
|
||||
OSC("OSC", "开源中国登录");
|
||||
|
||||
/**
|
||||
* 类型
|
||||
|
||||
@@ -105,4 +105,13 @@ public class SysUser implements Serializable {
|
||||
*/
|
||||
private String qqOpenid;
|
||||
|
||||
/**
|
||||
* 码云唯一标识
|
||||
*/
|
||||
private String giteeLogin;
|
||||
|
||||
/**
|
||||
* 开源中国唯一标识
|
||||
*/
|
||||
private String oscId;
|
||||
}
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.admin.handler;
|
||||
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.pig4cloud.pigx.admin.api.dto.UserInfo;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysSocialDetails;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysUser;
|
||||
import com.pig4cloud.pigx.admin.mapper.SysSocialDetailsMapper;
|
||||
import com.pig4cloud.pigx.admin.service.SysUserService;
|
||||
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.enums.LoginTypeEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2019/4/8
|
||||
* <p>
|
||||
* 码云登录
|
||||
*/
|
||||
@Slf4j
|
||||
@Component("GITEE")
|
||||
@AllArgsConstructor
|
||||
public class GiteeLoginHandler extends AbstractLoginHandler {
|
||||
private final SysSocialDetailsMapper sysSocialDetailsMapper;
|
||||
private final SysUserService sysUserService;
|
||||
|
||||
|
||||
/**
|
||||
* 码云登录传入code
|
||||
* <p>
|
||||
* 通过code 调用qq 获取唯一标识
|
||||
*
|
||||
* @param code
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String identify(String code) {
|
||||
SysSocialDetails condition = new SysSocialDetails();
|
||||
condition.setType(LoginTypeEnum.GITEE.getType());
|
||||
SysSocialDetails socialDetails = sysSocialDetailsMapper.selectOne(new QueryWrapper<>(condition));
|
||||
|
||||
String url = String.format(SecurityConstants.GITEE_AUTHORIZATION_CODE_URL, code
|
||||
, socialDetails.getAppId(), URLUtil.encode(socialDetails.getRedirectUrl()), socialDetails.getAppSecret());
|
||||
String result = HttpUtil.post(url, new HashMap<>(0));
|
||||
log.debug("码云响应报文:{}", result);
|
||||
|
||||
String accessToken = JSONUtil.parseObj(result).getStr("access_token");
|
||||
String userUrl = String.format(SecurityConstants.GITEE_USER_INFO_URL, accessToken);
|
||||
String resp = HttpUtil.get(userUrl);
|
||||
log.debug("码云获取个人信息返回报文{}", resp);
|
||||
|
||||
JSONObject userInfo = JSONUtil.parseObj(resp);
|
||||
//码云唯一标识
|
||||
String login = userInfo.getStr("login");
|
||||
return login;
|
||||
}
|
||||
|
||||
/**
|
||||
* identify 获取用户信息
|
||||
*
|
||||
* @param identify identify
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public UserInfo info(String identify) {
|
||||
|
||||
|
||||
SysUser user = sysUserService
|
||||
.getOne(Wrappers.<SysUser>query()
|
||||
.lambda().eq(SysUser::getGiteeLogin, identify));
|
||||
|
||||
if (user == null) {
|
||||
log.info("码云未绑定:{}", identify);
|
||||
return null;
|
||||
}
|
||||
return sysUserService.findUserInfo(user);
|
||||
}
|
||||
}
|
||||
+111
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright (c) 2018-2025, lengleng All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the pig4cloud.com developer nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
* Author: lengleng (wangiegie@gmail.com)
|
||||
*/
|
||||
|
||||
package com.pig4cloud.pigx.admin.handler;
|
||||
|
||||
import cn.hutool.http.HttpUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.pig4cloud.pigx.admin.api.dto.UserInfo;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysSocialDetails;
|
||||
import com.pig4cloud.pigx.admin.api.entity.SysUser;
|
||||
import com.pig4cloud.pigx.admin.mapper.SysSocialDetailsMapper;
|
||||
import com.pig4cloud.pigx.admin.service.SysUserService;
|
||||
import com.pig4cloud.pigx.common.core.constant.SecurityConstants;
|
||||
import com.pig4cloud.pigx.common.core.constant.enums.LoginTypeEnum;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author lengleng
|
||||
* @date 2019/4/8
|
||||
* <p>
|
||||
* 开源中国登录
|
||||
*/
|
||||
@Slf4j
|
||||
@Component("OSC")
|
||||
@AllArgsConstructor
|
||||
public class OscChinaLoginHandler extends AbstractLoginHandler {
|
||||
private final SysSocialDetailsMapper sysSocialDetailsMapper;
|
||||
private final SysUserService sysUserService;
|
||||
|
||||
|
||||
/**
|
||||
* 开源中国传入code
|
||||
* <p>
|
||||
* 通过code 调用qq 获取唯一标识
|
||||
*
|
||||
* @param code
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public String identify(String code) {
|
||||
SysSocialDetails condition = new SysSocialDetails();
|
||||
condition.setType(LoginTypeEnum.OSC.getType());
|
||||
SysSocialDetails socialDetails = sysSocialDetailsMapper.selectOne(new QueryWrapper<>(condition));
|
||||
|
||||
Map<String, Object> params = new HashMap<>(8);
|
||||
|
||||
params.put("client_id", socialDetails.getAppId());
|
||||
params.put("client_secret", socialDetails.getAppSecret());
|
||||
params.put("grant_type", "authorization_code");
|
||||
params.put("redirect_uri", socialDetails.getRedirectUrl());
|
||||
params.put("code", code);
|
||||
params.put("dataType", "json");
|
||||
|
||||
String result = HttpUtil.post(SecurityConstants.OSC_AUTHORIZATION_CODE_URL, params);
|
||||
log.debug("开源中国响应报文:{}", result);
|
||||
|
||||
String accessToken = JSONUtil.parseObj(result).getStr("access_token");
|
||||
|
||||
String url = String.format(SecurityConstants.OSC_USER_INFO_URL, accessToken);
|
||||
String resp = HttpUtil.get(url);
|
||||
log.debug("开源中国获取个人信息返回报文{}", resp);
|
||||
|
||||
JSONObject userInfo = JSONUtil.parseObj(resp);
|
||||
//开源中国唯一标识
|
||||
String id = userInfo.getStr("id");
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* identify 获取用户信息
|
||||
*
|
||||
* @param identify 开源中国表示
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public UserInfo info(String identify) {
|
||||
|
||||
|
||||
SysUser user = sysUserService
|
||||
.getOne(Wrappers.<SysUser>query()
|
||||
.lambda().eq(SysUser::getOscId, identify));
|
||||
|
||||
if (user == null) {
|
||||
log.info("开源中国未绑定:{}", identify);
|
||||
return null;
|
||||
}
|
||||
return sysUserService.findUserInfo(user);
|
||||
}
|
||||
}
|
||||
-1
@@ -41,7 +41,6 @@ import org.springframework.web.client.RestTemplate;
|
||||
@Component("WX")
|
||||
@AllArgsConstructor
|
||||
public class WeChatLoginHandler extends AbstractLoginHandler {
|
||||
private final RestTemplate restTemplate;
|
||||
private final SysUserService sysUserService;
|
||||
private final SysSocialDetailsMapper sysSocialDetailsMapper;
|
||||
|
||||
|
||||
+10
-1
@@ -25,6 +25,7 @@ import com.pig4cloud.pigx.admin.handler.LoginHandler;
|
||||
import com.pig4cloud.pigx.admin.mapper.SysSocialDetailsMapper;
|
||||
import com.pig4cloud.pigx.admin.mapper.SysUserMapper;
|
||||
import com.pig4cloud.pigx.admin.service.SysSocialDetailsService;
|
||||
import com.pig4cloud.pigx.common.core.constant.enums.LoginTypeEnum;
|
||||
import com.pig4cloud.pigx.common.security.util.SecurityUtils;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -56,7 +57,15 @@ public class SysSocialDetailsServiceImpl extends ServiceImpl<SysSocialDetailsMap
|
||||
public Boolean bindSocial(String type, String code) {
|
||||
String identify = loginHandlerMap.get(type).identify(code);
|
||||
SysUser sysUser = sysUserMapper.selectById(SecurityUtils.getUser().getId());
|
||||
sysUser.setWxOpenid(identify);
|
||||
if (LoginTypeEnum.GITEE.getType().equals(type)) {
|
||||
sysUser.setGiteeLogin(identify);
|
||||
} else if (LoginTypeEnum.OSC.getType().equals(type)) {
|
||||
sysUser.setOscId(identify);
|
||||
} else if (LoginTypeEnum.WECHAT.getType().equals(type)) {
|
||||
sysUser.setWxOpenid(identify);
|
||||
} else if (LoginTypeEnum.QQ.getType().equals(type)) {
|
||||
sysUser.setQqOpenid(identify);
|
||||
}
|
||||
sysUserMapper.updateById(sysUser);
|
||||
//更新緩存
|
||||
cacheManager.getCache("user_details").evict(sysUser.getUsername());
|
||||
|
||||
Reference in new issue
Block a user