【React-native】基于 DES 动态 Token 验证的设计方案

版权声明:请通知博主([email protected])获取允许后进行分享。 https://blog.csdn.net/qq934235475/article/details/84334239

前提:由于现有框架已经形成,但是需要增加在每次调用api的时候,进行token认证,认证通过才能允许访问接口。以防止越权访问。所以,准备在代价最小的情况下,进行修改。


总体思路:

1,在app登录成功后,服务端生成一个长达200的字符序列(不重复,当然也可以是5001000)存入数据库,同时返回给app

2,app拿到序列后,每次访问接口,在header里用该序列中的一个加上用户id(密码+用户id)进行des加密后生成token。

3,服务端拦截器获取该token,进行校验,如果通过,则允许访问接口,并且在数据库中删除该序列中的该密码(防止重复使用)。


详细设计:

一,新建数据库表

数据库新增一张USER_AUTHORITY表,用于存储用户id和对于的密码序列,不必担心数据量大,在用户退出删除该记录,在用户登录之前,会重置一次该序列。

表结构很简单,如下:


二,密码序列的生成

对于密码序列的生成,这里是截取UUID的前八位,然后需要的长度是在调用的时候输入(当然,你可以把这个长度设置放到数据库或者配置文件里,如果以后不够,可以轻松的增长。)。如下:

	private static Set<String> productAuthoritys(int num){
		Set<String> setString = new TreeSet<String>();
		for(int i = setString.size() ;i<num;i++) {
			setString.add(UUID.randomUUID().toString().replace("-", "").substring(0, 8));
		}
		return setString;
	}

这里直接明文返回给app端即可,因为app端会对此密码进行再加密后使用。 


三,APP存储密码序列

登录成功后,app对获取到的密码序列进行存储,这里使用的是AsyncStorage存储到本地。

由于AsyncStorage存储的是字符串,故这里做了一点转换。

 AsyncStorage.setItem("AuthorityPools", JSON.stringify(responseData.AuthorityPools).replace(/"/g,"").replace("[","").replace("]",""), function (error) {
                if (error) {
                    alert('存储失败');
                } else {
                    console.log('chenggong');
                }});

四,请求header中加入token 

在每次访问时,header里面加入token

每次需要获取新的authorityCode加入token中,(此处DES为加密方法,这里每个人的DES均不同,就不贴了,如有需要,可以留言或联系我)。

//通用请求中header
const _getHeader = () => {
    const authorityCode = getServerTime();
    //未加密前的数据
    let proCode = DES.encryptForToken(AppStore.getUserID());
    let token = DES.encryptForToken(this.authorityCode + '@' + proCode);
    return {
        'Accept': 'application/json',
        'Content-Type': 'application/x-www-form-urlencoded;application/json;',
        'Token': token,
    };
};

成功之后,会把该AuthorityPools(密码序列)中的一个authorityCode拿出来进行使用,然后删除该authorityCode后重新存储。

//授权码
this.authorityCode = "";
function getServerTime() {
    //调用接口增加 token ,从本地存储获取,避免越权。
    let AuthorityPools = AsyncStorage.getItem('AuthorityPools', function (error, result) {
        result = result == null ? "" :result;
        this.authorityCode = result.split(",")[0];
        AsyncStorage.setItem("AuthorityPools", result.replace(this.authorityCode + ",", ""), function (error) {
            if (error) {
                Alert.alert(
                    '错误',
                    '获取token数据失败',
                    [
                        {
                            text: '重新登录',
                            onPress: () => AppDispatcher.dispatch({actionType: AppConstants.FORCE_LOGOUT}),
                            type: 'plain-text'
                        },
                        {text: '取消', onPress: () => console.log('取消'), style: 'cancel'}
                    ]
                );
            } else {
                console.log('get authorityCode success!');
            }
        });
    })
    return AuthorityPools;
}

五,服务端Filter进行拦截校验。

这里,可以对get进行进行放行,只拦截除了get以外的请求。如下,获取headertoken

String token = DES.quickDecrypt(req.getHeader("Token") == null ? "" : req.getHeader("Token"))
					.toLowerCase().trim();

此后,就取出数据库中该用户的密码序列进行比较,比较成功后,放行,不成功则拒绝访问。

注:用户id取决于token中携带的用户id而不是session中的id。


六,可能会遇到的问题

1,由于AsyncStorage是异步存储,可以会出现上一次访问的密码序列,在本次才会进行调用。我这里的解决方案是在登录成功后,任意调用一个get接口一次即可解决异步。当然,还有async/await 的方式,但我试了没有用,可能我版本太低。


总结,这个方案搞了三天,之前走了很多弯路子。如每次访问之前请求一次时间戳(太卡,而且请求是回调请求,获取的时间戳不同步。)

此方案可以说很好的解决了我的问题,此外还有其他的加密方式,如JWT,但是JWT不能实时的变化,如果该访问被拦截后,使用该Token可以继续进行访问。

Bingo~

猜你喜欢

转载自blog.csdn.net/qq934235475/article/details/84334239
DES