废话少说,直接上代码。。。
1.对外提供第三方顶级接口
/**
* 对外提供第三方接口
* @author reyco
* @date 2019年8月9日
*
*/
public interface DevelopService {
/**
* 重定向授权登录页
* @param client_id id
* @param response_type 默认code
* @param redirect_uri 回调地址
* @return
*/
Result authorize(String client_id,String response_type,String redirect_uri);
/**
* 验证登录,生成token信息
* @param username 用户名
* @param password 密码
* @return
*/
Result setToken(String username,String password);
/**
* 生成token信息
* @param client_id id
* @param client_secret key
* @param redirect_uri 回调地址
* @param code 登录成功返回的code
* @param grant_type 默认authorization_code
* @return
* @throws Exception
*/
Result access_token(String client_id,String client_secret,String redirect_uri,String code,String grant_type) throws Exception;
/**
* 生成用户信息
* @param access_token token
* @return
*/
Result get_user_info(String access_token);
}
2.实现类
/**
* 对外第三方接口实现类
* @author reyco
* @date 2019年8月14日
*
*/
@Service("developService")
public class DevelopServiceImpl implements DevelopService {
/**
* 第三方绑定运用绑定
*/
@Autowired
private ApplicationService applicationService;
/**
* 用户信息
*/
@Autowired
private AccountsService accountsService;
/**
* redis
*/
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Override
public Result authorize(String client_id, String response_type, String redirect_uri) {
Result result = new Result();
// 1. 非空判断
if (StringUtils.isBlank(client_id) || StringUtils.isBlank(redirect_uri) || StringUtils.isBlank(response_type)) {
result.setMsg("参数错误");
return result;
}
// 2. 验证参数
Application application = applicationService.searchApplication(client_id);
// 2.1 验证失败
if (null == application) {
result.setMsg("应用不存在");
return result;
}
if (!application.getRedirect_uri().equals(redirect_uri)) {
result.setMsg("回调地址错误");
return result;
}
if (!response_type.equals("code")) {
result.setMsg("response_type错误");
return result;
}
// 2.2 验证成功
result.setIsTrue(true);
return result;
}
@Override
public Result setToken(String username, String password) {
// 1. 验证登录
Result result = accountsService.isLogin(username, password);
if (!result.isTrue()) {
return result;
}
// 2. 获取用户信息
Accounts accounts = (Accounts) result.getData();
// 3. 生成token
String code = UUID.randomUUID().toString().replace("-", "");
// 3.1 生成token
String token = JWTUtils.createJWT(code, "token", JWTUtils.generalSubject(accounts), 1000 * 10 * 60);
// 3.2 token放入redis,有效时间10分钟
ValueOperations<String, String> opsForValue = redisTemplate.opsForValue();
opsForValue.set(code, token, 10 * 60, TimeUnit.SECONDS);
// 4. 响应
result.setData(code);
return result;
}
@Override
public Result access_token(String client_id, String client_secret, String redirect_uri, String code,
String grant_type) throws Exception {
Result result = new Result();
// 1. 非空判断
if (StringUtils.isBlank(code) || StringUtils.isBlank(client_id) || StringUtils.isBlank(redirect_uri)
|| StringUtils.isBlank(client_secret) || StringUtils.isBlank(grant_type)) {
result.setMsg(JSONResult.ERROR_PARAMS);
return result;
}
// 2. 获取applicationResult对象
Result applicationResult = applicationService.getApplication(client_id);
// 3. 获取applicationResult对象是否成功
if (!applicationResult.isTrue()) {
result.setMsg(JSONResult.ERROR_PARAMS);
return result;
}
// 4. 获取applicationResult对象成功后,获取application对象
Application application = (Application) applicationResult.getData();
// 参数是否有误
if (!application.getRedirect_uri().equals(redirect_uri) || !application.getClient_secret().equals(client_secret)
|| !"authorization_code".equals(grant_type)) {
result.setMsg(JSONResult.ERROR_PARAMS);
return result;
}
// 5. 验证token
// 5.1 token是否存在
if (!redisTemplate.hasKey(code)) {
result.setMsg(JSONResult.ERROR_PARAMS);
return result;
}
ValueOperations<String, String> opsForValue = redisTemplate.opsForValue();
// 5.2 根据code获取token
String token = opsForValue.get(code);
// 5.3 验证token信息
JWTResult validateJWT = JWTUtils.validateJWT(token);
// 获取claims信息
Claims claims = validateJWT.getClaims();
// 5.4 token是否失效
// 失效时间
Date expirationDate = claims.getExpiration();
// 当前时间
Date nowDate = new Date();
if (nowDate.after(expirationDate)) {
result.setMsg("token已失效");
return result;
}
// 6. 生成token信息
String subject = claims.getSubject();
// 6.1 获取用户信息
Accounts accounts = JsonUtils.jsonToPojo(subject, Accounts.class);
String uid = accounts.getUid();
// 6.2 token存储对象
JSONObject tokenObj = new JSONObject();
long expirationTime = expirationDate.getTime();
long nowtime = nowDate.getTime();
// token有效时间
long expires_in = (expirationTime - nowtime);
tokenObj.put("access_token", token);
tokenObj.put("expires_in", "" + expires_in);
tokenObj.put("remind_in", "1234");
tokenObj.put("uid", uid);
result.setData(tokenObj);
result.setIsTrue(true);
return result;
}
@Override
public Result get_user_info(String access_token) {
Result result = new Result();
try {
// 1. 非空判断
if (StringUtils.isBlank(access_token)) {
result.setMsg(JSONResult.ERROR_PARAMS);
return result;
}
// 2. 验证token信息
JWTResult validateJWT = JWTUtils.validateJWT(access_token);
// 2.1 获取claims信息
Claims claims = validateJWT.getClaims();
// 2.2 token失效时间
Date expirationDate = claims.getExpiration();
// 当前时间
Date nowDate = new Date();
if (nowDate.after(expirationDate)) {
result.setMsg("token已失效");
return result;
}
// 获取token主题信息
String subject = claims.getSubject();
// 3. 获取用户信息
Accounts accounts = JsonUtils.jsonToPojo(subject, Accounts.class);
// 3.1 user存储对象
JSONObject userInfoObj = new JSONObject();
userInfoObj.put("nickname", accounts.getNickname());
userInfoObj.put("scope", "");
userInfoObj.put("create_at", accounts.getGmtCreate());
userInfoObj.put("uid", accounts.getUid());
result.setData(userInfoObj);
result.setIsTrue(true);
return result;
} catch (JSONException e) {
e.printStackTrace();
}
result.setMsg("token无效");
return result;
}
}
3.controller
/**y
* 模拟第三方登录开发接口
* @author reyco
* @date 2019年7月26日
*
*/
@RequestMapping("oauth2")
@Controller
public class DevelopController {
@Autowired
private DevelopService developService;
/**
* 重定向授权登录页
* @param client_id id
* @param response_type 响应类型默认code
* @param redirect_uri 第三方回调地址
* @param request
* @return
* @throws IOException
*/
@RequestMapping("authorize")
public String authorize(String client_id,String response_type,String redirect_uri) throws IOException {
// 验证参数
Result result = developService.authorize(client_id, response_type, redirect_uri);
// 验证参数有误
if(!result.isTrue()) {
return "../error.html";
}
// 验证参数无误,重定向登录页面
return "../login.html";
}
/**
* 验证登录,生成token
* @param username 用户名
* @param password 密码
* @param redirect_uri 第三方回调地址(建议前端放在请求头中)
* @return
*/
@ResponseBody
@RequestMapping("login")
public String login(String username,String password,String redirect_uri) {
// 验证登录,设置token
Result result = developService.setToken(username, password);
// 验证登录失败
if(!result.isTrue()) {
return JSONResult.failCreate("用户名或密码错误", result.getMsg()).toJSON();
}
// 验证登录失败
String code = (String)result.getData();
if(!redirect_uri.contains("?")) {
redirect_uri = redirect_uri+"?code="+code;
}else {
redirect_uri = redirect_uri+"&code="+code;
}
return JSONResult.create(redirect_uri).toJSON();
}
/**
* 获取token信息
* @param code 登录验证成功返回的code
* @param client_id id
* @param client_secret key
* @param grant_type 默认authorization_code
* @param redirect_uri 第三方回调地址
* @param request
* @return
* @throws Exception
*/
@ResponseBody
@RequestMapping("access_token")
public String access_token(String code,String client_id,String client_secret,String grant_type,String redirect_uri,HttpServletRequest request) throws Exception {
// 生成token信息
Result accessTokenResult = developService.access_token(client_id,client_secret,redirect_uri, code, grant_type);
// 失败
if(!accessTokenResult.isTrue()) {
return accessTokenResult.getMsg();
}
// 成功,响应token
Object token = accessTokenResult.getData();
return token.toString();
}
/**
* 获取用户信息
* @param access_token token
* @param request
* @return
* @throws JSONException
*/
@ResponseBody
@RequestMapping("get_user_info")
public String get_user_info(String access_token,HttpServletRequest request) throws JSONException {
// 生成用户信息
Result get_user_info_result = developService.get_user_info(access_token);
// 失败
if(!get_user_info_result.isTrue()) {
return get_user_info_result.getMsg();
}
// 成功,响应用户信息
Object token = get_user_info_result.getData();
return token.toString();
}
}
4.配置文件
#dyw
dyw.id=a5f452dddd19d8ba
dyw.key=6e72a3e8974b44ad8a4de3c14a6abbbc
dyw.authorize_url=http://login.test.com/oauth2/authorize.do
dyw.callback=http://login.test.com/login/callback.do?type=dyw
dyw.get_token_url=http://login.test.com/oauth2/access_token.do
dyw.get_token_info=http://login.test.com/oauth2/get_user_info.do
5.本地web应用对第三方登录的实现
5.1登录接口:
/**
* 第三方登录顶级接口
*
* @author reyco
* @date 2019年7月23日
*
*/
public interface LoginService {
/**
* 登录 ---返回重定向地址
* @return
*/
String login()throws Exception;
/**
* 回调地址
* @param code 登录成功后返回的code
* @param request 参数request
* @return
* @throws Exception
*/
String callback(String code,HttpServletRequest request,HttpServletResponse response) throws Exception;
}
5.2实现:
@Service("dyw")
public class DYWLoginServiceImpl implements LoginService{
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private AccountService accountService;
@Autowired
private RestTemplate restTemplate;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Override
public String login() throws Exception {
// 回调地址
String backUrl = PropertiesUtil.getValue("dyw.callback");
String authorize_url = PropertiesUtil.getValue("dyw.authorize_url");
// appid
String dywID = PropertiesUtil.getValue("dyw.id");
String state = UUID.randomUUID().toString().replaceAll("-", "");
String url=authorize_url
+ "?client_id="+dywID
+ "&response_type=code"
+ "&redirect_uri="+URLEncoder.encode(backUrl)
+ "&state="+state;
return url;
}
@Override
public String callback(String code, HttpServletRequest request, HttpServletResponse response) throws Exception {
JSONObject accessToken = getAccessToken(code);
String token = accessToken.getString("access_token");
JSONObject userInfo = getUserInfo(token);
String uid = userInfo.getString("uid");
String nickname = userInfo.getString("nickname");
boolean flag = accountService.searchCountByPhone(uid);
if(flag) {
// 用户信息
Integer type=Type.DYW;
Integer integral = 0;
String phone=uid;
String name = nickname;
String password = "e10adc3949ba59abbe56e057f20f883e";
Integer status = Status.NORMAL;
Integer gender = 1;
String desc = "点击修改备注";
//注册用户
accountService.register(type, integral, phone, password, name, gender, null, null, status, desc);
}
Account account = accountService.getAccount(uid);
// 获取登录用户
String loginPhone = account.getPhone();
// 是否其它客户端已登录---如果已登录则强制退出
ValueOperations<String, Object> opsForValue = redisTemplate.opsForValue();
if(redisTemplate.hasKey(loginPhone)) {
// 判断redis中是否存在该用户key,并获取
Object phoneValue = opsForValue.get(loginPhone);
redisTemplate.delete(loginPhone);
// 判断redis中是否存在cookie
if(redisTemplate.hasKey(phoneValue.toString())) {
redisTemplate.delete(phoneValue.toString());
}
}
Integer id = account.getId();
Integer vipId = account.getVipId();
// 获取登录ip
String ip = CusAccessObjectUtil.getIpAddress(request);
// 获取登录城市
String cityName = IPDataUtils.getCityName(ip);
String loginName = account.getName();
User u = new User(id,loginPhone,vipId,ip, cityName, loginName);
// 生成cookie的key
String key = UUID.randomUUID().toString().replace("-", "");
// 保存到redis
opsForValue.set(key, JsonUtils.objectToJson(u),30*60,TimeUnit.SECONDS);
opsForValue.set(loginPhone, key,30*60,TimeUnit.SECONDS);
// 生成cookie
CookieUtil.setCookie(request, response, "user_token", key, -1);
return "http://login.test.com/user/index.html";
}
/**
* 获取token信息
* @param code
* @return
* @throws Exception
*/
private JSONObject getAccessToken(String code) throws Exception {
String dywID = PropertiesUtil.getValue("dyw.id");
String dywKey = PropertiesUtil.getValue("dyw.key");
String callback = PropertiesUtil.getValue("dyw.callback");
String get_token_url = PropertiesUtil.getValue("dyw.get_token_url");
get_token_url=get_token_url+"?grant_type=authorization_code" + "&client_id="
+ dywID + "&client_secret=" + dywKey + "&redirect_uri=" + callback + "&code=" + code;
String tokenObject = restTemplate.getForObject(get_token_url,String.class);
JSONObject dywTokenObject = new JSONObject(tokenObject);
logger.info("dyw."+dywTokenObject.toString());
return dywTokenObject;
}
/**
* 获取用户信息
* @param token
* @return
* @throws Exception
*/
private JSONObject getUserInfo(String token) throws Exception {
String get_userInfo_url = PropertiesUtil.getValue("dyw.get_token_info");
String get_user_info_url=get_userInfo_url+"?access_token="+token;
String userInfoObject = restTemplate.getForObject(get_user_info_url,String.class);
JSONObject dywUserInfoObject = new JSONObject(userInfoObject);
logger.info("dyw."+dywUserInfoObject.toString());
return dywUserInfoObject;
}
}
5.3登录策略:
@Component("loginServiceContext")
public class LoginServiceContext {
@Autowired
private Map<String,LoginService> loginSerivceMap = new HashMap<String,LoginService>();
/**
* 重定向地址
* @param type
* @return
* @throws Exception
*/
public String login(String type) throws Exception {
return loginSerivceMap.get(type).login();
}
/**
* 回调地址
* @param type
* @param request
* @param response
* @return
* @throws Exception
*/
public String callback(String code,String type,HttpServletRequest request,HttpServletResponse response) throws Exception {
return loginSerivceMap.get(type).callback(code,request,response);
}
}
5.4controller
@RequestMapping("login")
@Controller
public class LoginController {
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private LoginServiceContext loginServiceContext;
@RequestMapping("login")
public String login(String type) throws Exception {
String redirectUrl = loginServiceContext.login(type);
logger.info(redirectUrl);
return "redirect:"+redirectUrl;
}
@RequestMapping("callback")
public String callback(HttpServletRequest request,HttpServletResponse response,String code,String type) throws Exception {
String callback = loginServiceContext.callback(code,type, request,response);
return "redirect:"+callback;
}
}
5.5对外提供第三方登录
<html>
<head>
<link rel="shortcut icon"type="image/x-icon" href="../imags/logo1.png" media="screen" />
<style type="text/css">
#search-button{
width: 50px;
text-align: center;
}
#search-button{
opacity:0.9;
border:1px solid #2d78f4;
background-color:#3385ff;
}
#search-button:hover{
opacity:1.0;
background-color:#317ef3;
border:1px solid #2868c8;
}
.btn-primary {
color: #3385ff;
text-decoration: none;
}
.btn-primary:hover{
color: #2d78f4;
text-decoration: underline;
}
</style>
<script type="text/javascript" src="../jQuery/jquery-3.2.1.js"></script>
<script src="../jQuery/jQuery.md5.js"></script>
<script type="text/javascript">
/* 根据参数名获取参数值*/
function getQueryString(name){
var reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)");
var r = window.location.search.substr(1).match(reg);
if(r!=null)return unescape(r[2]); return null;
}
$(function(){
load();
})
function load(){
var client_id = getQueryString("client_id");
$.ajax({
type:'post',
dataType: "json",
url:"../application/getApplicationName.do",
data:{
client_id:client_id,
time:new Date()
},
success:function(data){
var flag = data.success;
if(flag){
$("#application").text(data.data.info);
}else{
}
},
error:function(){
}
});
}
function login(){
var redirect_uri = getQueryString("redirect_uri");
var username = $("#username").val();
var password = $("#password").val();
$.ajax({
type:'post',
dataType: "json",
url:"../oauth2/login.do",
data:{
redirect_uri:redirect_uri,
username:username,
password:$.md5(password),
time:new Date()
},
success:function(data){
var flag = data.success;
if(flag){
window.location.replace(data.data.info);
}else{
$("#msg").html("<font color='red'>"+data.data.msg+"</font>")
}
},
error:function(){
}
});
}
</script>
</head>
<body>
<h1>对外提供第三方登录接口本地测试,其中一个账号:reyco,密码:123456。</h1>
<div>
<input id="redirect_uri" type="hidden" name="redirect_uri"><span id=msg></span></br>
<input id="username" type="text" name="username"></br>
<input id="password" type="text" name="password"></br>
<input type="button" value="login" onclick="login()">
</div>
<div align="left">
<div>
<p class=""></p>
<p class=""></p>
<p class="">
<a id="application" class="btn-primary" href="http://www.test.com" target="_blank"></a>
将获得以下权限
</p>
<div class="" id="">
<ul>
<li>
<input type="checkbox" id="select_all" hidefocus="true" checked="checked"/>
<label for="select_all">全选</label>
</li>
<li>
<input type="checkbox" hidefocus="true" id="item_80901010" title="默认授权 不可更改" hidefocus="true" checked disabled/>
<label for="item_80901010" class="oauth_item_title">获得您的昵称、头像、性别</label>
</li>
</ul>
</div>
<div>
<p>
授权后表明你已同意
<a href="" class="btn-primary" target="_blank">dyw登录服务协议</a>
</p>
</div>
<div>
<p>
<a href="http://www.test.com/manage.html" class="btn-primary" target="_blank">dyw登录</a>
<span>|</span>
<a href="http://www.test.com/manage.html" class="btn-primary" target="_blank">授权管理</a>
<span>|</span>
<a href="http://www.test.com/manage.html" class="btn-primary" target="_blank">申请接入</a>
</p>
</div>
</div>
</div>
</body>
</html>
5.7调用第三方登录按照:
<div><a href="../login/login.do?type=dyw" style="float: right;border-right: 100px" class="btn btn-link text-muted"><img src="../imags/dl.png"/></a></div>
<div><a href="../login/login.do?type=qq" style="float: right;border-right: 100px" class="btn btn-link text-muted"><img src="../imags/qq.png"/></a></div>
<div><a href="../login/login.do?type=baidu" style="float: right;border-right: 100px" class="btn btn-link text-muted"><img src="../imags/wx.png"/></a></div>
<div><a href="../login/login.do?type=weibo" style="float: right;border-right: 100px" class="btn btn-link text-muted"><img src="../imags/wb.png"/></a></div>