一、简述
本人当前负责的Java后台系统是这样的:
用户登录的操作在某几台服务器上,进行实际业务处理的在另几台服务器上(有多类,每类有多台,例如考试系统服务器集群、问卷系统服务器集群等);
同一个集群的服务器之前配置了session共享(基于redis),但是不同的服务器之间是没有的。
例如,当用户准备登录问卷系统,首先由前端带着用户名密码请求登录服务器,登录服务器会根据用户登录参数、生成一个签名,返回给前端;前端带着签名与其它参数请求问卷服务器,问卷服务器会判断这个签名是否真实有效,如果有效,则允许继续操作,否则返回错误信息。
个人理解,使用签名验签的好处是,可以把登录流程汇总在一个单独的登录系统中,其余系统不必保存用户的账号密码等敏感信息,同时便于增加新系统新功能。(不知道是不是这样)
接下来总结下验签流程。
二、验签具体流程
-
用户访问登录页面(前端),输入用户名密码,点击登录,跳转到登录服务器后台的/login接口,传入的参数为:用户名name,密码password,当前时间的时间戳t,后续要跳转的前端页面url (例如用户登录后要跳转到问卷系统,那就是问卷的index.html)。
-
登录服务器/login接口中,从数据库或redis查询,判断用户名密码是否正确;
如果不正确,则不允许登录;
如果正确,则使用以下4个参数生成一个签名sign:
(1)用户名name
(2)时间戳t
(3)后续要跳转的前端页面url
(4)从本地获取一个秘钥字符串s
-
登录服务器把name、t、url、s这4个参数拼接成一个字符串,然后使用sha256加密,再使用md5加密,得到签名sign。
●sha256与md5是通用的方法,会把传入的字符串加密成密文;混入一个s后,由于s保存在服务器本地,外部无法得到,因此生成的密文就难以伪造了。
-
登录服务器返回给前端签名sign
-
前端使用刚得到的sign,连同之前的name、t、url,共4个参数,发送给问卷服务器后端/authorize接口。
-
问卷服务器首先对时间戳t进行校验,如果超时,则不允许访问,返回错误信息;
如果没有超时,则继续下一步。
-
问卷服务器从本地获取一个秘钥字符串s(与登录服务器获取的相同,可以约定配置一个相同的),然后把秘钥s、用户名name、时间戳t、url这4个参数拼接成一个字符串,先进行sha256加密,再进行md5加密,得到一个字符串。(加密方式也与登录服务器相同)
-
把得到的字符串与签名sign对比,如果相同,说明参数合法,可以进行下一步操作。
-
问卷服务器继续处理,首先根据用户名name尝试从redis获取一个long类型的userid,如果获取到了,说明问卷服务器还保存着该用户的登录信息,把(“USER_ID”,userid)存入session;
如果没有从redis获取到,就根据name查询问卷服务器的数据库中的用户信息表,如果查询到了,就把数据库中的long类型的id作为userid,存入redis,然后把(“USER_ID”,userid)存入session;
如果从数据库中没有获取到id,那就insert一条,同时把得到的id作为userid,存入redis,然后把(“USER_ID”,userid)存入session。
●虽然问卷服务器不用保存用户的密码,但是也得保存下用户的账号信息,不然连是谁操作了问卷系统都不知道;所以把name转为了userid,作为用户在问卷服务器中的唯一标识。
●redis的作用是判断用户是否在数据库中保存过了信息
●java中执行insert操作同时获取id的方法:
//如果insert成功,则id会被封装到user对象中;user对象是自己写的javabean对象。
//底层可能还是需要执行2句sql,一句insert一句select;不过java中只写一句insert就够了。
int insert = userMapper.insert(user);
if(insert == 1){
Long userid = user.getId();
}
-
问卷服务器继续处理,如果上述操作成功,则session中有了(“USER_ID”,userid);然后执行response.sendRedirect(url)方法,重定向到前端页面。(url是之前的参数)
-
此时,浏览器进入了问卷index.html前端页面,这个页面会继续向问卷服务器的其它后端接口发送请求,获得必要的信息,例如列表接口/list。
这是前端第二次请求问卷服务器后台,请求参数中已经无需再传sign等敏感参数,仅仅是Cookie中带有sessionid。
-
问卷服务器后台的拦截器先收到请求,这个拦截器会拦截/authorize、错误页等以外的所有请求;
拦截器中,会从session中获取(“USER_ID”)的值,如果获取到,说明用户已登录,把值放入ThreadLocal中,供后续使用,然后放行;
如果没有获取到,则拒绝访问。
-
现在问卷服务器/list接口收到了请求,可以从ThreadLocal中获取到USER_ID的值,从数据库中查询到返回结果,返回给前端了。
●当然也可以从session中得到USER_ID的值,不过为了方便,后续还有很多其它类要用到其它参数,因此使用了ThreadLocal保存变量;否则就得一直把参数对象传递到需要使用的地方了。
-
后续用户访问问卷服务器,只需要cookie中携带sessionid;服务器只需要判断session中是否存在USER_ID即可,如果存在,说明用户处于登录状态,可以提供服务;如果不存在,则拒绝访问。
●如果用户近期登录过、并且session没有超时,此时使用cookie中的sessionid即可伪装成该用户。
●本地自测时经常用到,先在页面登录一下,获取到cookie,然后打开postman,输入url、请求参数、cookie,即可访问后台的其它接口;当新写的接口还没有通全流程、不好直接从网页访问时,就可以这样测试。
●不过cookie丢失的可能性还是很小的,毕竟得用户刚登录过、没有超时,所以还是安全的。
三、总结
验签流程总结:
-
前端携带用户名密码与其它参数,请求登录服务器
-
登录服务器生成签名sign,返回给前端
-
前端携带sign与其它参数,请求问卷服务器
-
问卷服务器验证签名sign是否正确,如果正确,则把userid存入session,继续提供后续服务;如果错误,则拒绝访问
-
后续用户访问问卷服务器,只需要判断session中是否存在userid,如果存在,说明用户处于登录状态,可以提供服务;如果不存在,则拒绝提供服务