session简介
做过Web开发的程序员应该对Session都⽐较熟悉,Session是⼀块保存在服务器端的内存空间,⼀般⽤于保存⽤户的会话信息。
⽤户通过⽤户名和密码登陆成功之后,服务器端程序会在服务器端开辟⼀块Session内存空间并将⽤户的信息存⼊这块空间,同时服务器会在cookie中写⼊⼀个Session_id的值,这个值⽤于标识这个内存空间。
下次⽤户再来访问的话会带着这个cookie中的session_id,服务器拿着这个id去寻找对应的session,如果session中已经有了这个⽤户的登陆信息,则说明⽤户已经登陆过了。
使⽤Session保持会话信息使⽤起来⾮常简单,技术也⾮常成熟。但是也存在下⾯的⼏个问题:
服务器压⼒⼤:通常Session是存储在内存中的,每个⽤户通过认证之后都会将session数据保存在服务器的内存中,⽽当⽤户量增⼤时,服务器的压⼒增⼤。
Session共享:现在很多应⽤都是分布式集群,需要我们做额外的操作进⾏Session共享;
CSRF跨站伪造请求攻击:Session机制是基于浏览器端的cookie的,cookie如果被截获,⽤户就会很容易受到跨站请求伪造的攻击。
基于token的认证
基于token的认证机制将认证信息返回给客户端并存储。下次访问其他页⾯,需要从客户端传递认证信息回服务端。简单的流程如下:
客户端使⽤⽤户名跟密码请求登录;服务端收到请求,去验证⽤户名与密码;
验证成功后,服务端会签发⼀个 Token,再把这个 Token 发送给客户端;
客户端收到 Token 以后可以把它存储起来,⽐如放在 Cookie ⾥或者 Local Storage ⾥;客户端每次向服务端请求资源的时候需要带着服务端签发的 Token;
服务端收到请求,然后去验证客户端请求⾥⾯带着的 Token,如果验证成功,就向客户端返回请求的数据;基于token的验证机制,有以下的优点:
⽀持跨域访问,将token置于请求头中,⽽cookie是不⽀持跨域访问的;
⽆状态化,服务端⽆需存储token,只需要验证token信息是否正确即可,⽽session需要在服务端存储,⼀般是通过cookie中的sessionID在服务端查找对应的session;
⽆需绑定到⼀个特殊的⾝份验证⽅案(传统的⽤户名密码登陆),只需要⽣成的token是符合我们预期设定的即可;
更适⽤于移动端(Android,iOS,⼩程序等等),像这种原⽣平台不⽀持cookie,⽐如说微信⼩程序,每⼀次请求都是⼀次会话,当然我们可以每次去⼿动为他添加cookie,详情请查看博主另⼀篇博客;避免CSRF跨站伪造攻击,还是因为不依赖cookie;
缺点的话⼀个就是相⽐较于传统的session登陆机制实现起来略微复杂⼀点,另外⼀个⽐较⼤的缺点是由于服务器不保存 token,因此⽆法在使⽤过程中废⽌某个 token,或者更改 token 的权限。也就是说,⼀旦 token 签发了,在到期之前就会始终有效,除⾮服务器部署额外的逻辑。退出登陆的话,只要前端清除token信息即可。
基于JWT的token认证实现
JWT(JSON Web Token)就是基于token认证的代表,这边就⽤JWT为列来介绍基于token的认证机制。需要引⼊JWT的依赖
⽣成token和验证token的⼯具类如下:
public class JWTTokenUtil { //设置过期时间
private static final long EXPIRE_DATE=30*60*100000; //token秘钥
private static final String TOKEN_SECRET = \"ZCfasfhuaUUHufguGuwu2020BQWE\"; public static String token (String username,String password){
String token = \"\"; try {
//过期时间
Date date = new Date(System.currentTimeMillis()+EXPIRE_DATE); //秘钥及加密算法
Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET);
//设置头部信息
Map //携带username,password信息,⽣成签名 token = JWT.create() .withHeader(header) .withClaim(\"username\ .withClaim(\"password\ .sign(algorithm); }catch (Exception e){ e.printStackTrace(); return null; } return token; } public static boolean verify(String token){ /** * @desc 验证token,通过返回true * @params [token]需要校验的串 **/ try { Algorithm algorithm = Algorithm.HMAC256(TOKEN_SECRET); JWTVerifier verifier = JWT.require(algorithm).build(); DecodedJWT jwt = verifier.verify(token); return true; }catch (Exception e){ e.printStackTrace(); return false; } } public static void main(String[] args) { String username =\"name1\"; String password = \"pw1\"; //注意,⼀般不会把密码等私密信息放在payload中,这边只是举个列⼦ String token = token(username,password); System.out.println(token); boolean b = verify(token); System.out.println(b); }} 执⾏结果如下: Connected to the target VM, address: '127.0.0.1:11838', transport: 'socket' eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwYXNzd29yZCI6IjEyMyIsImV4cCI6MTU5NzM5Nzc0OCwidXNlcm5hbWUiOiJ6aGFuZ3NhbiJ9.LI5S_nX-YcqtExI9UtKiP8FPqpQW_ccaws2coLzyOS0true 关于DecodedJWT这个类,⼤家可以重点看下,⾥⾯包含了解码后的⽤户信息。 JWT的使⽤说明 客户端收到服务器返回的 JWT,可以储存在 Cookie ⾥⾯,也可以储存在 localStorage。 此后,客户端每次与服务器通信,都要带上这个 JWT。你可以把它放在 Cookie ⾥⾯⾃动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求的头信息Authorization字段⾥⾯。 Authorization: Bearer 另⼀种做法是,跨域的时候,JWT 就放在 POST 请求的数据体⾥⾯。 JWT 本⾝包含了认证信息,⼀旦泄露,任何⼈都可以获得该令牌的所有权限。为了减少盗⽤,JWT 的有效期应该设置得⽐较短。对于⼀些⽐较重要的权限,使⽤时应该再次对⽤户进⾏认证。 为了减少盗⽤,JWT 不应该使⽤ HTTP 协议明码传输,要使⽤ HTTPS 协议传输。(或者是对JWT在前后端之间进⾏加密之后在传输) 关于JWT的⼀个问题 上⾯⽣成JWT token的过程关键点就是密钥,假如这个密钥泄露了,那是不是就可以伪造token了。 还有就是⽣产环境的密钥值,开发的程序员⼤概率是知道的,怎么防⽌程序要监守⾃盗,伪造token值呢?希望有经验的⼤佬指教下。 //token秘钥 private static final String TOKEN_SECRET = \"ZCfasfhuaUUHufguGuwu2020BQWE\"; 关于上⾯的问题,@仙湖码农 给出了⼀个简单易懂的⽅案~ jwt 来⽣成token,还有⼀个玩法,⽤户登录时,⽣成token的 SecretKey 是⼀个随机数,也就是说每个⽤户,每次登录时jwt SecretKey 是随机数,并保存到缓存,key是登录账户,(当然了,分布式缓存的话,就⽤Redis,sqlserver缓存等等),总之,客户端访问接⼝是,header 要带登录账户,和token,服务端拿到登录账号,到缓存去捞相应的SecretKey ,然后再进⾏token校验。可以防伪造token了(这个⽅案在⼀定程度上能防⽌伪造,但是不能防⽌token泄露被劫持)。获取refresh token的⽅法#参考# 以上所述是⼩编给⼤家介绍的Token登陆验证机制的原理及实现,希望对⼤家有所帮助。在此也⾮常感谢⼤家对⽹站的⽀持! 因篇幅问题不能全部显示,请点此查看更多更全内容