JustAuth升级到v1.8.1版本,新增AuthState工具类,可自动生成state

本文主要是介绍JustAuth升级到v1.8.1版本,新增AuthState工具类,可自动生成state,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

JustAuth(gitee | github),如你所见,它仅仅是一个第三方授权登录的工具类库,它可以让我们脱离繁琐的第三方登录SDK,让登录变得So easy!

JustAuth的功能

史上最全的整合第三方登录的工具,目前已支持Github、Gitee、微博、钉钉、百度、Coding、腾讯云开发者平台、OSChina、支付宝、QQ、微信、淘宝、Google、Facebook、抖音、领英、小米、微软和今日头条等第三方平台的授权登录。 Login, so easy!

JustAuth特点

废话不多说,就俩字:

  1. :已集成十多家第三方平台(国内外常用的基本都已包含),后续依然还有扩展计划!
  2. :API就是奔着最简单去设计的(见后面快速开始),尽量让您用起来没有障碍感!

JustAuth(gitee | github)今日升级到1.8.1版本,新增了AuthState工具类,支持根据source自动生成state

AuthState.java

package me.zhyd.oauth.utils;import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.exception.AuthException;
import me.zhyd.oauth.request.ResponseStatus;import java.nio.charset.Charset;
import java.util.concurrent.ConcurrentHashMap;/*** state工具,负责创建、获取和删除state** @author yadong.zhang (yadong.zhang0415(a)gmail.com)* @version 1.0* @since 1.8*/
@Slf4j
public class AuthState {/*** 空字符串*/private static final String EMPTY_STR = "";/*** state存储器*/private static ConcurrentHashMap<String, String> stateBucket = new ConcurrentHashMap<>();/*** 生成随机的state** @param source oauth平台* @return state*/public static String create(String source) {return create(source, RandomUtil.randomString(4));}/*** 创建state** @param source oauth平台* @param body   希望加密到state的消息体* @return state*/public static String create(String source, Object body) {return create(source, JSON.toJSONString(body));}/*** 创建state** @param source oauth平台* @param body   希望加密到state的消息体* @return state*/public static String create(String source, String body) {String currentIp = getCurrentIp();String simpleKey = ((source + currentIp));String key = Base64.encode(simpleKey.getBytes(Charset.forName("UTF-8")));log.debug("Create the state: ip={}, platform={}, simpleKey={}, key={}, body={}", currentIp, source, simpleKey, key, body);if (stateBucket.containsKey(key)) {log.debug("Get from bucket: {}", stateBucket.get(key));return stateBucket.get(key);}String simpleState = source + "_" + currentIp + "_" + body;String state = Base64.encode(simpleState.getBytes(Charset.forName("UTF-8")));log.debug("Create a new state: {}", state, simpleState);stateBucket.put(key, state);return state;}/*** 获取state** @param source oauth平台* @return state*/public static String get(String source) {String currentIp = getCurrentIp();String simpleKey = ((source + currentIp));String key = Base64.encode(simpleKey.getBytes(Charset.forName("UTF-8")));log.debug("Get state by the key[{}], current ip[{}]", key, currentIp);return stateBucket.get(key);}/*** 获取state中保存的body内容** @param source oauth平台* @param state  加密后的state* @param clazz  body的实际类型* @param <T>    需要转换的具体的class类型* @return state*/public static <T> T getBody(String source, String state, Class<T> clazz) {if (StringUtils.isEmpty(state) || null == clazz) {return null;}log.debug("Get body from the state[{}] of the {} and convert it to {}", state, source, clazz.toString());String currentIp = getCurrentIp();String decodedState = Base64.decodeStr(state);log.debug("The decoded state is [{}]", decodedState);if (!decodedState.startsWith(source)) {return null;}String noneSourceState = decodedState.substring(source.length() + 1);if (!noneSourceState.startsWith(currentIp)) {// ip不相同,可能为非法的请求throw new AuthException(ResponseStatus.ILLEGAL_REQUEST);}String body = noneSourceState.substring(currentIp.length() + 1);log.debug("body is [{}]", body);if (clazz == String.class) {return (T) body;}if (clazz == Integer.class) {return (T) Integer.valueOf(Integer.parseInt(body));}if (clazz == Long.class) {return (T) Long.valueOf(Long.parseLong(body));}if (clazz == Short.class) {return (T) Short.valueOf(Short.parseShort(body));}if (clazz == Double.class) {return (T) Double.valueOf(Double.parseDouble(body));}if (clazz == Float.class) {return (T) Float.valueOf(Float.parseFloat(body));}if (clazz == Boolean.class) {return (T) Boolean.valueOf(Boolean.parseBoolean(body));}if (clazz == Byte.class) {return (T) Byte.valueOf(Byte.parseByte(body));}return JSON.parseObject(body, clazz);}/*** 登录成功后,清除state** @param source oauth平台*/public static void delete(String source) {String currentIp = getCurrentIp();String simpleKey = ((source + currentIp));String key = Base64.encode(simpleKey.getBytes(Charset.forName("UTF-8")));log.debug("Delete used state[{}] by the key[{}], current ip[{}]", stateBucket.get(key), key, currentIp);stateBucket.remove(key);}private static String getCurrentIp() {String currentIp = IpUtils.getIp();return StringUtils.isEmpty(currentIp) ? EMPTY_STR : currentIp;}
}

AuthState基本用法

工具类的基本用法如下:

@Test
public void usage() {String source = "github";System.out.println("\nstep1 生成state: 预期创建一个新的state...");String state = AuthState.create(source);System.out.println(state);System.out.println("\nstep2 重复生成state: 预期从bucket中返回一个可用的state...");String recreateState = AuthState.create(source);System.out.println(recreateState);Assert.assertEquals(state, recreateState);System.out.println("\nstep3 获取state: 预期获取上面生成的state...");String stateByBucket = AuthState.get(source);System.out.println(stateByBucket);Assert.assertEquals(state, stateByBucket);System.out.println("\nstep4 删除state: 预期删除掉上面创建的state...");AuthState.delete(source);System.out.println("\nstep5 重新获取state: 预期返回null...");String deletedState = AuthState.get(source);System.out.println(deletedState);Assert.assertNull(deletedState);
}

输出结果:

step1 生成state: 预期创建一个新的state...
Z2l0aHViXzE5Mi4xNjguMTkuMV9lemV5step2 重复生成state: 预期从bucket中返回一个可用的state...
Z2l0aHViXzE5Mi4xNjguMTkuMV9lemV5step3 获取state: 预期获取上面生成的state...
Z2l0aHViXzE5Mi4xNjguMTkuMV9lemV5step4 删除state: 预期删除掉上面创建的state...step5 重新获取state: 预期返回null...
null

基础版的创建state的方法,适用于对state无复杂要求,只做简单验证时使用,内部通过source_ip_randomstr的方式生成state

AuthState扩展用法

到这一步,可能有朋友会觉得,这功能太鸡肋了,限制太大了,只能生成固定格式的随机字符串,没法利用state做一些复杂的业务操作。我只想说,“莫慌~~”,这才哪到哪。
如果需要对state做特殊处理,比如要在state中保存当前用户信息(id)、当前页面的标识(bind-oauth)等更多的特殊参数时,可以使用以下方法:

  • create(String source, String body)
public static String create(String source, String body) {return xx;
}

这个方法接收一个string类型的变量, 可以传入复杂的字符串类型, 也可以自己封装数据后转成字符串,然后调用这个接口。当然,针对非字符串的特殊结构参数,可以使用下面的方法。

  • create(String source, Object body)
public static String create(String source, Object body) {return create(source, JSON.toJSONString(body));
}

这个方法接收一个Object类型的变量,支持任意一种数据类型,可以传入map、list、实体类等复杂结构的参数。

接下来演示一下通过AuthState生成随机的、指定字符串的或者指定任意内容的state字符串。用法如下:

@Test
public void create() {String source = "github";System.out.println("\n通过随机字符串生成state...");String state = AuthState.create(source);System.out.println(state);AuthState.delete(source);System.out.println("\n通过传入自定义的字符串生成state...");String stringBody = "这是一个字符串";String stringState = AuthState.create(source, stringBody);System.out.println(stringState);AuthState.delete(source);System.out.println("\n通过传入数字生成state...");Integer numberBody = 111;String numberState = AuthState.create(source, numberBody);System.out.println(numberState);AuthState.delete(source);System.out.println("\n通过传入日期生成state...");Date dateBody = DateUtil.parse("2019-01-01 12:12:12", DatePattern.NORM_DATETIME_PATTERN);String dateState = AuthState.create(source, dateBody);System.out.println(dateState);AuthState.delete(source);System.out.println("\n通过传入map生成state...");Map<String, Object> mapBody = new HashMap<>();mapBody.put("userId", 1);mapBody.put("userToken", "xxxxx");String mapState = AuthState.create(source, mapBody);System.out.println(mapState);AuthState.delete(source);System.out.println("\n通过传入List生成state...");List<String> listBody = new ArrayList<>();listBody.add("xxxx");listBody.add("xxxxxxxx");String listState = AuthState.create(source, listBody);System.out.println(listState);AuthState.delete(source);System.out.println("\n通过传入实体类生成state...");AuthConfig entityBody = AuthConfig.builder().clientId("xxxxx").clientSecret("xxxxx").build();String entityState = AuthState.create(source, entityBody);System.out.println(entityState);AuthState.delete(source);
}

输出结果:

通过随机字符串生成state...
Z2l0aHViXzE5Mi4xNjguMTkuMV81Y3pz通过传入自定义的字符串生成state...
Z2l0aHViXzE5Mi4xNjguMTkuMV/ov5nmmK/kuIDkuKrlrZfnrKbkuLI=通过传入数字生成state...
Z2l0aHViXzE5Mi4xNjguMTkuMV8xMTE=通过传入日期生成state...
Z2l0aHViXzE5Mi4xNjguMTkuMV8xNTQ2MzE1OTMyMDAw通过传入map生成state...
Z2l0aHViXzE5Mi4xNjguMTkuMV97InVzZXJUb2tlbiI6Inh4eHh4IiwidXNlcklkIjoxfQ==通过传入List生成state...
Z2l0aHViXzE5Mi4xNjguMTkuMV9bInh4eHgiLCJ4eHh4eHh4eCJd通过传入实体类生成state...
Z2l0aHViXzE5Mi4xNjguMTkuMV97ImNsaWVudElkIjoieHh4eHgiLCJjbGllbnRTZWNyZXQiOiJ4eHh4eCIsInVuaW9uSWQiOmZhbHNlfQ==

怎么样? 是不是很爽?想往state中插入任何数据都可以。但是有些朋友可能又会说了:你这只生成了state,但是我怎么获取到我传入的具体的body内容?我就算生成的,但是我用不到这个数据,一样是“脱了裤子放屁 ——多此一举”。

咳~~ 还是那俩字:“莫慌~~”

获取自定的body

前面介绍到了, 可以通过create(String source, String body)或者create(String source, Object body)生成复杂类型的state,那么如何从encode之后的state中获取到定制的body内容呢?

AuthState提供了获取body内容的方法:

/*** 获取state中保存的body内容** @param source oauth平台* @param state  加密后的state* @param clazz  body的实际类型* @param <T>    需要转换的具体的class类型* @return state*/
public static <T> T getBody(String source, String state, Class<T> clazz) {...
}

方法接收三个参数,参数的作用如上注释,只要是按照规定规则({source}_{ip}_{body})生成的state,都可以通过getBody方法解析出body内容。

示例代码:

@Test
public void getBody() {String source = "github";System.out.println("\n通过随机字符串生成state...");String state = AuthState.create(source);System.out.println(state);String body = AuthState.getBody(source, state, String.class);System.out.println(body);AuthState.delete(source);System.out.println("\n通过传入自定义的字符串生成state...");String stringBody = "这是一个字符串";String stringState = AuthState.create(source, stringBody);System.out.println(stringState);stringBody = AuthState.getBody(source, stringState, String.class);System.out.println(stringBody);AuthState.delete(source);System.out.println("\n通过传入数字生成state...");Integer numberBody = 111;String numberState = AuthState.create(source, numberBody);System.out.println(numberState);numberBody = AuthState.getBody(source, numberState, Integer.class);System.out.println(numberBody);AuthState.delete(source);System.out.println("\n通过传入日期生成state...");Date dateBody = DateUtil.parse("2019-01-01 12:12:12", DatePattern.NORM_DATETIME_PATTERN);String dateState = AuthState.create(source, dateBody);System.out.println(dateState);dateBody = AuthState.getBody(source, dateState, Date.class);System.out.println(dateBody);AuthState.delete(source);System.out.println("\n通过传入map生成state...");Map<String, Object> mapBody = new HashMap<>();mapBody.put("userId", 1);mapBody.put("userToken", "xxxxx");String mapState = AuthState.create(source, mapBody);System.out.println(mapState);mapBody = AuthState.getBody(source, mapState, Map.class);System.out.println(mapBody);AuthState.delete(source);System.out.println("\n通过传入List生成state...");List<String> listBody = new ArrayList<>();listBody.add("xxxx");listBody.add("xxxxxxxx");String listState = AuthState.create(source, listBody);System.out.println(listState);listBody = AuthState.getBody(source, listState, List.class);System.out.println(listBody);AuthState.delete(source);System.out.println("\n通过传入实体类生成state...");AuthConfig entityBody = AuthConfig.builder().clientId("xxxxx").clientSecret("xxxxx").build();String entityState = AuthState.create(source, entityBody);System.out.println(entityState);entityBody = AuthState.getBody(source, entityState, AuthConfig.class);System.out.println(entityBody);AuthState.delete(source);
}

运行结果:

通过随机字符串生成state...
Z2l0aHViXzE5Mi4xNjguMTkuMV9maXBo
fiph通过传入自定义的字符串生成state...
Z2l0aHViXzE5Mi4xNjguMTkuMV/ov5nmmK/kuIDkuKrlrZfnrKbkuLI=
这是一个字符串通过传入数字生成state...
Z2l0aHViXzE5Mi4xNjguMTkuMV8xMTE=
111通过传入日期生成state...
Z2l0aHViXzE5Mi4xNjguMTkuMV8xNTQ2MzE1OTMyMDAw
Tue Jan 01 12:12:12 CST 2019通过传入map生成state...
Z2l0aHViXzE5Mi4xNjguMTkuMV97InVzZXJUb2tlbiI6Inh4eHh4IiwidXNlcklkIjoxfQ==
{userToken=xxxxx, userId=1}通过传入List生成state...
Z2l0aHViXzE5Mi4xNjguMTkuMV9bInh4eHgiLCJ4eHh4eHh4eCJd
[xxxx, xxxxxxxx]通过传入实体类生成state...
Z2l0aHViXzE5Mi4xNjguMTkuMV97ImNsaWVudElkIjoieHh4eHgiLCJjbGllbnRTZWNyZXQiOiJ4eHh4eCIsInVuaW9uSWQiOmZhbHNlfQ==
me.zhyd.oauth.config.AuthConfig@725bef66

如上,通过getBody获取到具体的body数据后,调用方可以根据自己的规则去校验state或者做其他逻辑操作。

作者:咋样? 这回没有问题了吧?
刺头:切,你有能耐让代码按照我的意念生成任意格式的state?
作者:噗~~(一口老痰喷他一脸)

How to use?

前面主要通过源码+示例解释了AuthState的用法,那么我们在实际使用JustAuth时,只需要将state参数改为以下方式即可:

new AuthGithubRequest(AuthConfig.builder().clientId("xx").clientSecret("xx").redirectUri("https://www.zhyd.me/oauth/callback/github").state(AuthState.create(source)) // 1.8.1版本提供的生成state的方法 .build());

注意:授权登录后,需要手动清除本次请求流程中生成的state

AuthState.delete(source);

通过JustAuth-demo运行测试后的输出内容:

进入render:github
IP:192.168.19.1
2019-07-15 19:10:59 [me.zhyd.oauth.utils.AuthState:65] DEBUG - Create the state: ip=192.168.19.1, platform=github, simpleKey=github192.168.19.1, key=Z2l0aHViMTkyLjE2OC4xOS4x, body=n8gn
2019-07-15 19:10:59 [me.zhyd.oauth.utils.AuthState:74] DEBUG - Create a new state: Z2l0aHViXzE5Mi4xNjguMTkuMV9uOGdu
https://github.com/login/oauth/authorize?client_id=xxx&amp;redirect_uri=http://dblog-web.zhyd.me/oauth/callback/github&amp;state=Z2l0aHViXzE5Mi4xNjguMTkuMV9uOGdu
进入callback:github callback params:{"code":"609c1df58e66da9eb5c6","state":"Z2l0aHViXzE5Mi4xNjguMTkuMV9uOGdu"}
2019-07-15 19:11:00 [me.zhyd.oauth.utils.AuthState:106] DEBUG - Get body from the state[Z2l0aHViXzE5Mi4xNjguMTkuMV9uOGdu] of the github and convert it to class java.lang.String
2019-07-15 19:11:00 [me.zhyd.oauth.utils.AuthState:109] DEBUG - The decoded state is [github_192.168.19.1_n8gn]
2019-07-15 19:11:00 [me.zhyd.oauth.utils.AuthState:119] DEBUG - body is [n8gn]
获取state中的body信息:n8gn
IP:192.168.19.1
2019-07-15 19:11:00 [me.zhyd.oauth.utils.AuthState:65] DEBUG - Create the state: ip=192.168.19.1, platform=github, simpleKey=github192.168.19.1, key=Z2l0aHViMTkyLjE2OC4xOS4x, body=2s6v
2019-07-15 19:11:00 [me.zhyd.oauth.utils.AuthState:68] DEBUG - Get from bucket: Z2l0aHViXzE5Mi4xNjguMTkuMV9uOGdu
{"code":2000,"data":{"avatar":"https://avatars3.githubusercontent.com/u/12689082?v=4","blog":"https://www.zhyd.me","company":"innodev","email":"yadong.zhang0415@gmail.com","gender":"UNKNOW","location":"Beijing","nickname":"yadong.zhang","remark":"心之所向,无所不能","source":"GITHUB","token":{"accessToken":"xx","expireIn":0},"username":"zhangyd-c","uuid":"xx"}}
2019-07-15 19:11:08 [me.zhyd.oauth.utils.AuthState:157] DEBUG - Delete used state[Z2l0aHViXzE5Mi4xNjguMTkuMV9uOGdu] by the key[Z2l0aHViMTkyLjE2OC4xOS4x], current ip[192.168.19.1]

项目源码

  • https://gitee.com/yadong.zhang/JustAuth
  • https://github.com/zhangyd-c/JustAuth

其他开源作品

  • blog-hunter,一款简单好用并且支持多个平台的博客爬取工具
  • OneBlog,一个简洁美观、功能强大并且自适应的Java博客
  • JustAuth,史上最全的整合第三方登录的工具,目前已支持Github、Gitee、微博、钉钉、百度、Coding、腾讯云开发者平台、OSChina、支付宝、QQ、微信、淘宝、Google、Facebook、抖音、领英、小米、微软和今日头条等第三方平台的授权登录。Login, so easy!
  • spingboot-shiro,Springboot + shiro权限管理。这或许是流程最详细、代码最干净、配置最简单的shiro上手项目了。
  • braum-spring-boot-starter,Braum可以很方便的帮助开发人员过滤、识别恶意请求

这篇关于JustAuth升级到v1.8.1版本,新增AuthState工具类,可自动生成state的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1020132

相关文章

揭秘未来艺术:AI绘画工具全面介绍

📑前言 随着科技的飞速发展,人工智能(AI)已经逐渐渗透到我们生活的方方面面。在艺术创作领域,AI技术同样展现出了其独特的魅力。今天,我们就来一起探索这个神秘而引人入胜的领域,深入了解AI绘画工具的奥秘及其为艺术创作带来的革命性变革。 一、AI绘画工具的崛起 1.1 颠覆传统绘画模式 在过去,绘画是艺术家们通过手中的画笔,蘸取颜料,在画布上自由挥洒的创造性过程。然而,随着AI绘画工

墨刀原型工具-小白入门篇

墨刀原型工具-小白入门篇 简介 随着互联网的发展和用户体验的重要性越来越受到重视,原型设计逐渐成为了产品设计中的重要环节。墨刀作为一款原型设计工具,以其简洁、易用的特点,受到了很多设计师的喜爱。本文将介绍墨刀原型工具的基本使用方法,以帮助小白快速上手。 第一章:认识墨刀原型工具 1.1 什么是墨刀原型工具 墨刀是一款基于Web的原型设计工具,可以帮助设计师快速创建交互原型,并且可以与团队

大学湖北中医药大学法医学试题及答案,分享几个实用搜题和学习工具 #微信#学习方法#职场发展

今天分享拥有拍照搜题、文字搜题、语音搜题、多重搜题等搜题模式,可以快速查找问题解析,加深对题目答案的理解。 1.快练题 这是一个网站 找题的网站海量题库,在线搜题,快速刷题~为您提供百万优质题库,直接搜索题库名称,支持多种刷题模式:顺序练习、语音听题、本地搜题、顺序阅读、模拟考试、组卷考试、赶快下载吧! 2.彩虹搜题 这是个老公众号了 支持手写输入,截图搜题,详细步骤,解题必备

ONLYOFFICE 8.1 版本桌面编辑器测评

在现代办公环境中,办公软件的重要性不言而喻。从文档处理到电子表格分析,再到演示文稿制作,强大且高效的办公软件工具能够极大提升工作效率。ONLYOFFICE 作为一个功能全面且开源的办公软件套件,一直以来都受到广大用户的关注与喜爱。而其最新发布的 ONLYOFFICE 8.1 版本桌面编辑器,更是带来了诸多改进和新特性。本文将详细评测 ONLYOFFICE 8.1 版本桌面编辑器,探讨其在功能、用户

17.用300行代码手写初体验Spring V1.0版本

1.1.课程目标 1、了解看源码最有效的方式,先猜测后验证,不要一开始就去调试代码。 2、浓缩就是精华,用 300行最简洁的代码 提炼Spring的基本设计思想。 3、掌握Spring框架的基本脉络。 1.2.内容定位 1、 具有1年以上的SpringMVC使用经验。 2、 希望深入了解Spring源码的人群,对 Spring有一个整体的宏观感受。 3、 全程手写实现SpringM

Windows/macOS/Linux 安装 Redis 和 Redis Desktop Manager 可视化工具

本文所有安装都在macOS High Sierra 10.13.4进行,Windows安装相对容易些,Linux安装与macOS类似,文中会做区分讲解 1. Redis安装 1.下载Redis https://redis.io/download 把下载的源码更名为redis-4.0.9-source,我喜欢跟maven、Tomcat放在一起,就放到/Users/zhan/Documents

android 带与不带logo的二维码生成

该代码基于ZXing项目,这个网上能下载得到。 定义的控件以及属性: public static final int SCAN_CODE = 1;private ImageView iv;private EditText et;private Button qr_btn,add_logo;private Bitmap logo,bitmap,bmp; //logo图标private st

iptables(7)扩展模块state

简介         前面文章我们已经介绍了一些扩展模块,如iprange、string、time、connlimit、limit,还有扩展匹配条件如--tcp-flags、icmp。这篇文章我们介绍state扩展模块  state          在 iptables 的上下文中,--state 选项并不是直接关联于一个扩展模块,而是与 iptables 的 state 匹配机制相关,特

Visual Studio中,MSBUild版本问题

假如项目规定了MSBUild版本,那么在安装完Visual Studio后,假如带的MSBUild版本与项目要求的版本不符合要求,那么可以把需要的MSBUild添加到系统中,然后即可使用。步骤如下:            假如项目需要使用V12的MSBUild,而安装的Visual Studio带的MSBUild版本为V14。 ①到MSDN下载V12 MSBUild包,把V12包解压到目录(

OpenCompass:大模型测评工具

大模型相关目录 大模型,包括部署微调prompt/Agent应用开发、知识库增强、数据库增强、知识图谱增强、自然语言处理、多模态等大模型应用开发内容 从0起步,扬帆起航。 大模型应用向开发路径:AI代理工作流大模型应用开发实用开源项目汇总大模型问答项目问答性能评估方法大模型数据侧总结大模型token等基本概念及参数和内存的关系大模型应用开发-华为大模型生态规划从零开始的LLaMA-Factor