Java实现Aligenie天猫精灵OAuth2.0认证授权流程
时间:2019-02-16
本文章向大家介绍Java实现Aligenie天猫精灵OAuth2.0认证授权流程,主要包括Java实现Aligenie天猫精灵OAuth2.0认证授权流程使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
由于对智能家居物联网比较感兴趣,之前参考电子发烧友上 小狂的教程,加上自己的想法,完善一版智能家居的设备平台实现,
且天猫精灵的开放性和功能是国内智能音箱中的佼佼者,准备用java重构之前被魔改混乱的php代码。
实现OAuth2.0接入:
参考文档:天猫精灵开放平台OAuth2.0
先实现5个类:
Authorize.java //认证服务,用来发放code授权码
GetToken.java //授权服务,用来发放token访问令牌
CodeFactory.java //生成随机code和token
TokenBean.java //为了生成携带token的json数据
SqlOperat.java //提供与数据库进行存取数据接口
此处实现的是授权码模式,具体流程为:
1、用户点击打开授权请求的按钮/链接(url携带回调地址redirect_uri和一些信息)
2、浏览器用这个链接向Authorize请求授权页面
3、Authorize响应授权页面给浏览器并展示给用户
4、用户点击确认授权
5、Authorize收到用户确认后生成code授权码加上state参数,用302重定向到redirect_uri地址
6、浏览器收到后进行重定向带上code+state提交给redirect_uri这个地址(Aligenie的服务器)
7、Aligenie的服务器收到code后带上这个code参数 向GetToken请求申请token
8、GetToken对code的正确性和有效性进行验证,验证通过返回给Aligenie的服务器数据如下
{
"access_token": "XXXXXX", //访问令牌token,用来请求资源或者服务,就是通行证
"refresh_token": "XXXXXX", //获取下一次token的token,就是通行证过期后用它换新的通行证
"expires_in":17600000 //token的有效期
}
至此,授权过程完成
* * * * *No img you say JB* * * * *
下面测试授权成功:
回归实现,下面附上代码:
认证服务:
/**
* @author : willian fu
* @date : 2019-02-14.
* OAUTH授权服务
*/
@WebServlet("/authorize")
public class Authorize extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
System.out.println(req.getRequestURI());
HttpSession session = req.getSession();
//如果点击了确认授权
if ("yes".equals(req.getParameter("authorize"))){
//确认了授权,获取第三方接收code的重定向地址
String redirect_uri = (String) session.getAttribute("redirect_uri");
//System.out.println(redirect_uri);
String state = (String) session.getAttribute("state");
//生成code授权码
String code = CodeFactory.getCode();
String client_id = (String) session.getAttribute("client_id");
//发布访问令牌,解码url并且将code加在url后面返回
redirect_uri = UrlUtil.getURLDecoderString(redirect_uri)+"&code="+code+"&state="+state;
System.out.println(redirect_uri);
//保存code到数据库
SqlOperat.savaAccessCode(code, redirect_uri, client_id);
resp.setStatus(302);
resp.setHeader("location", redirect_uri);
}else if ("no".equals(req.getParameter("authorize"))){
resp.setHeader("refresh","2;url=/authorize");
resp.getWriter().write("<h2 style='text-align:center'>授权取消,请重新授权!</h2>");
}else {
//缓存重定向redirect_url地址到session
session.setAttribute("redirect_uri",req.getParameter("redirect_uri"));
session.setAttribute("state",req.getParameter("state"));
session.setAttribute("client_id",req.getParameter("client_id"));
//如果是授权请求,则展示授权页面
System.out.println("redirect_uri---"+session.getAttribute("redirect_uri"));
req.getRequestDispatcher("oauthShow.html").forward(req,resp);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
授权服务:
/**
* @author uthor : willian fu
* @date : 2019-02-14.
* Token发放服务
* errorCode :
* 1000 code过期
* 2000 grant_type类型错误
* 3000 OAuth用户名/密码错误
*/
@WebServlet("/token")
public class GetToken extends HttpServlet{
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
//resp.setContentType("text/html;charset=utf-8");
System.out.println("hello token!");
String client_id = req.getParameter("client_id");
String code = req.getParameter("code");
String redirect_uri = SqlOperat.queryOauthUser(client_id,"redirect_uri");
//校验url中OAuth用户名和密码及参数正确性
if (SqlOperat.queryOauthUser(client_id,"client_secret")
.equals(req.getParameter("client_secret"))){
//&& redirect_uri.equals(req.getParameter("redirect_uri"))
if ("authorization_code".equals(req.getParameter("grant_type"))) {
//获取客户端code ,校验code正确有效性
if (checkAccessCodeOk(code, client_id)) {
//发布token
String token = getJsonData(client_id);
resp.setContentType("application/json;charset=utf-8");
System.out.println(token);
resp.getWriter().write(token);
}else {
responseError(resp, 1000);
}
}else if("refresh_token".equals(req.getParameter("grant_type"))){
//验证refresh_token正确性
if (req.getParameter("refresh_token")
.equals(SqlOperat.queryLastOauthRefreshToken(client_id))){
String token = getJsonData(client_id);
resp.setContentType("application/json;charset=utf-8");
System.out.println(token);
resp.getWriter().write(token);
}
}else {responseError(resp, 2000);}
}else {
//返回错误响应
responseError(resp, 3000);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
doGet(req, resp);
}
/**
*
* @param client_id OAuth用户名
* @return token json
*/
private static String getJsonData(String client_id){
System.out.println("getJsonData-------");
//刷新access_token
String access_token = CodeFactory.getToken();
//刷新refresh_token
String refresh_token = CodeFactory.getToken();
//保存token记录
SqlOperat.saveAccessOrRefreshToken(access_token,true,client_id);
SqlOperat.saveAccessOrRefreshToken(refresh_token,false,client_id);
//token过期后进行刷新
String token = JSON.toJSONString(new TokenBean(access_token, 259200, refresh_token, null));
return token;
}
/**
* 返回错误响应
* @param resp s
*/
private static void responseError(HttpServletResponse resp, int errorCode) throws IOException {
resp.setContentType("application/json;charset=utf-8");
System.out.println("error!!");
//返回错误状态
resp.getWriter().write("{error\":"+errorCode+",\"error_description\":\"description}");
}
/**
* 校验code是否发放且正确未过期
* @param code code
* @param client_id oauth user
* @return code result?ok
*/
private static boolean checkAccessCodeOk(String code, String client_id){
String getCodeDate = SqlOperat.getAccessCode(code,client_id);
Date date = new Date();//获得系统时间.
String nowDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);
if (getCodeDate != null){
long codeTime = Long.parseLong(SqlOperat.dateToStamp(getCodeDate));
long nowTime = Long.parseLong(SqlOperat.dateToStamp(nowDate));
System.out.println(codeTime+"--"+nowTime+"="+(nowTime-codeTime));
if (codeTime != 0 && (nowTime - codeTime)/1000 <= 60*2){
return true;
}else {
return false;
}
}else {
return false;
}
}
}
token&&Code生成:
/**
* @author uthor : willian fu
* @date : 2019-02-14.
*
*/
public class CodeFactory {
/**
* 获取随机code
* @return code
*/
public static String getCode(){
//生成长度40的随机code
String code = getRandomString(40);
return code;
}
/**
* 获取随机token
* @return token
*/
public static String getToken(){
//生成长度42的随机token
String token = getRandomString(42);
return token;
}
/**
* 字符串抽取随机
* @param count
* @return 随机数
*/
private static int getRandom(int count) {
return (int) Math.round(Math.random() * (count));
}
//字符串模板
private static String string = "abcdefghijk123456789lmnopqrstuvwxyz";
/**
* 生成随机字符串
* @param length
* @return 随机字符串
*/
private static String getRandomString(int length){
StringBuffer sb = new StringBuffer();
int len = string.length();
for (int i = 0; i < length; i++) {
sb.append(string.charAt(getRandom(len-1)));
}
return sb.toString();
}
@Test
public void getCodeTest(){
System.out.println(getCode());
}
}
token信息实体类:
/**
* @author : willian fu
* @date : 2019-02-14
*/
public class TokenBean {
//token值
String access_token;
//有效时间
Integer expires_in;
//新token
String refresh_token;
//权限范围
@JSONField(serialize=false)
String scope;
public TokenBean(String access_token, Integer expires_in, String refresh_token, String scope) {
this.access_token = access_token;
this.expires_in = expires_in;
this.refresh_token = refresh_token;
this.scope = scope;
}
public String getAccess_token() {
return access_token;
}
public void setAccess_token(String access_token) {
this.access_token = access_token;
}
public Integer getExpires_in() {
return expires_in;
}
public void setExpires_in(Integer expires_in) {
this.expires_in = expires_in;
}
public String getRefresh_token() {
return refresh_token;
}
public void setRefresh_token(String refresh_token) {
this.refresh_token = refresh_token;
}
public String getScope() {
return scope;
}
public void setScope(String scope) {
this.scope = scope;
}
}
数据库操作类实现:
/**
* @author : willian fu
* @date : 2019-02-14.
* 数据库交互
*/
public class SqlOperat {
/**
* 静态加载连接池
*/
private static JdbcTemplate jdbcTemplate = JdbcUtil.getJdbcTemplate();
/**
* 查询Oauth用户数据库密码
* @param oauthUser Oauth用户
* @param name 字段名
* @return password
*/
public static String queryOauthUser(String oauthUser, String name){
String sql = "select * from oauth_clients where client_id=?";
Map<String, Object> oauthUserMap = jdbcTemplate.queryForMap(sql, oauthUser);
return (String) oauthUserMap.get(name);
}
/**
* 查询最后发布的AccessToken
* @param oauthUser 用户
* @return lastAccessToken
*/
public static String queryLastOauthAccessToken(String oauthUser){
String sql = "SELECT * FROM oauth_access_tokens WHERE expires=\n" +
"(SELECT MAX(expires) FROM oauth_access_tokens WHERE client_id=?)";
Map<String, Object> oauthUserMap = jdbcTemplate.queryForMap(sql, oauthUser);
return (String) oauthUserMap.get("access_token");
}
/**
* 查询最后发布的RefreshToken
* @param oauthUser 用户
* @return RefreshToken
*/
public static String queryLastOauthRefreshToken(String oauthUser){
String sql = "SELECT * FROM oauth_refresh_tokens WHERE expires=" +
"(SELECT MAX(expires) FROM oauth_refresh_tokens WHERE client_id=?)";
Map<String, Object> oauthUserMap = jdbcTemplate.queryForMap(sql, oauthUser);
return (String) oauthUserMap.get("refresh_token");
}
/**
* 保存Token
* @param token token
* @param type true-Access / false-Refresh
* @return result
*/
public static int saveAccessOrRefreshToken(String token, boolean type, String client_id){
String sqlr = "INSERT INTO oauth_refresh_tokens(refresh_token,client_id,expires) VALUES(?,?,?)";
String sqla = "INSERT INTO oauth_access_tokens(access_token,client_id,expires) VALUES(?,?,?)";
int rows = 0;
Date date = new Date();//获得系统时间.
String nowTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);//将时间格式转换成符合Timestamp要求的格式.
Timestamp nowDate =Timestamp.valueOf(nowTime);//把时间转换
if (type){
rows = jdbcTemplate.update(sqla, token,client_id,nowDate);
}else {
rows = jdbcTemplate.update(sqlr, token,client_id,nowDate);
}
return rows;
}
/**
* 保存code授权码
* @param code AccessCode
* @param redirect_uri 回调地址
* @param client_id OauthUsername
* @return Sql result
*/
public static int savaAccessCode(String code, String redirect_uri, String client_id){
String sql = "INSERT INTO oauth_authorization_codes (authorization_code,client_id,redirect_uri,expires) VALUES (?,?,?,?)";
Date date = new Date();//获得系统时间
String nowTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(date);//将时间格式转换成符合Timestamp要求的格式.
Timestamp nowDate =Timestamp.valueOf(nowTime);//把时间转换
int rows = jdbcTemplate.update(sql,code,client_id,redirect_uri,nowDate);
return rows;
}
public static String getAccessCode(String code, String client_id){
String sql = "SELECT * FROM oauth_authorization_codes WHERE expires=" +
"(SELECT MAX(expires) FROM oauth_authorization_codes WHERE client_id=?)";
Map<String, Object> oauthUserMap = jdbcTemplate.queryForMap(sql, client_id);
if (code.equals(oauthUserMap.get("authorization_code"))){
return oauthUserMap.get("expires").toString();
}else {
return null;
}
}
/**
* yyyy-MM-dd HH:mm:ss 格式转时间戳
* @param s
* @return Stamp
* @throws ParseException
*/
public static String dateToStamp(String s){
String res;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = null;
try {
date = simpleDateFormat.parse(s);
long ts = date.getTime();
res = String.valueOf(ts);
return res;
} catch (ParseException e) {
e.printStackTrace();
}
return "0";
}
/**
* 时间戳转时间
* @param s
* @return date
*/
public static String stampToDate(String s){
String res;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
long lt = new Long(s);
Date date = new Date(lt);
res = simpleDateFormat.format(date);
return res;
}
}
记得导包。。。。。。。。。。。
欢迎留言和纠错
- Android6.0源码分析之menu键弹出popupwindow菜单流程分析
- Android中初步自定义view
- Android中View研究自学之路 Android6.0源码分析之View(一)Android6.0源码分析之View(二)
- Android蓝牙配对弹出框过程分析 Android蓝牙配对弹出框过程分析
- Android6.0之修改或者查看系统属性值
- linux下Android7.0多用户编译问题
- 带你解锁蓝牙skill(0)
- WiFiAp探究实录--功能实现与源码分析
- Android7.1.1系统设置默认值大全
- Androidstudio编译c/c++jni方法
- 带你解锁蓝牙skill(三)
- 带你解锁蓝牙skill(二)
- 带你解锁蓝牙skill(一)
- Android四大组件完全解析(二)---Service
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- 二胖写参数校验的坎坷之路
- 图像倾斜校正算法的MATLAB实现:图像倾斜角检测及校正
- R语言时间序列数据指数平滑法分析交互式动态可视化
- R语言进阶之图形的合并
- R语言广义线性模型索赔频率预测:过度分散、风险暴露数和树状图可视化
- 还在使用Future轮询获取结果吗?CompletionService快来了解下。
- R语言通过伽玛与对数正态分布假设下的广义线性模型对大额索赔进行评估预测
- R语言精算学:使用链梯法Chain Ladder和泊松定律模拟和预测未来赔款数据
- 微服务[学成在线] day19:分布式事务
- 微服务[学成在线] day20:项目部署与持续集成(DevOps)
- R语言中回归模型预测的不同类型置信区间应用比较分析
- R语言进阶之坐标轴和文本
- R语言广义线性模型(GLM)广义相加模型(GAM):多元平滑回归分析保险投资风险敞口
- 面试高频题:springBoot自动装配的原理你能说出来吗?
- R语言巨灾风险下再保险合同定价研究案例:广义线性模型和帕累托分布分析