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;
    }
}

记得导包。。。。。。。。。。。

欢迎留言和纠错