加密算法实现

加密算法
加密算法可以归结为三大类:哈希算法、对称加密算法、非对称加密算法

1、DES算法进行加密解密;后来人们觉得DES不够安全,发明了3DES算法;而如今最为流行的对称加密算法是AES算法。
对称算法的好处是加密解密的效率比较高。相应的,对称算法的缺点是不够安全。

2、RSA算法是最常见的非对称加密算法。和对称加密算法比如DES的明显区别在于用于加密、解密的密钥是不同的。使用RSA算法,
只要密钥足够长(一般要求1024bit),加密的信息是不能被破解的。

3、信息摘要算法又叫加密散列算法,加密过程不需要密钥,常见的加密散列算法有MD系列和SHA系列。
从严格意义上来说,哈希算法并不属于加密算法,但它在信息安全领域起到了很重要的作用。最重要的作用就是生成信息摘要,
用以验证原信息的完整性和来源的可靠性。

一个理想的加密散列函数应该具备以下特性:
1、任何信息传入后,输出的总是长度固定;
2、消息摘要看起来是“随机的”,这样根据原始信息就很难推测出值;
3、好的散列函数碰撞概率应该极低,也就是不同信息传入后得到相同值的概率;

解决方案:在数据库中不直接存储口令原文,而是存储口令的密文。密文是通过原文执行消息摘要计算后的结果。可以考虑使用JDK
提供的md5算法进行加密处理,但是由于碰撞算法的发展,所以直接加密的结果可以通过碰撞算法进行解密。

解决方案:引入一个加密算法的参数–salt盐值,而且进行多次hash计算获取加密的指纹数据,可以在一定程度上方法使用碰撞
算法进行解密

JDK提供了md5和sha-1算法的实现,这里选择使用md5进行加密

前后端分离开发或者使用水平扩展部署多个应用时,用户跟踪就是个问题。
1、依赖于代理服务器Nginx实现
2、可以使用spring session依赖于redis实现
3、依赖于token身份验证方式实现

token就是一种身份验证方法,扩展性和安全性更高,非常适合用在Web应用和移动开发应用上。

1、token验证流程
使用token身份验证,服务器端就不会存储用户的登录记录。
(1)客户端使用用户名跟密码请求登录;
(2)服务端收到请求,去验证用户名与密码;
(3)验证成功后,服务端会签发一个Token,再把这个Token发送给客户端;
(4)客户端收到Token以后可以把它存储起来,比如放在Cookie里或者Local Storage里;
(5)客户端每次向服务端请求资源的时候需要带着服务端签发的Token;
(6)服务端收到请求,然后去验证客户端请求里面带着的Token,如果验证成功,就向客户端返回请求的数据。

token的认证方式相比传统的session认证方式更节约服务器资源,并且对移动端和分布式更加友好。其优点如下:
1、支持跨域访问:cookie是无法跨域的,而token由于没有用到cookie(前提是将token放到请求头中),所以跨域后
不会存在信息丢失问题
2、无状态:token机制在服务端不需要存储session信息,因为token自身包含了所有登录用户的信息,所以可以减轻服
务端压力
3、更适用CDN:可以通过内容分发网络请求服务端的所有资料
4、更适用于移动端:当客户端是非浏览器平台时,cookie是不被支持的,此时采用token认证方式会简单很多
5、无需考虑CSRF:由于不再依赖cookie,所以采用token认证方式不会发生CSRF,所以也就无需考虑CSRF的防御

2、JWT即json web token,是一个开放标准rfc7519,它定义了一种紧凑的、自包含的方式,用于在各方之间以JSON对象
安全地传输信息。它是以JSON形式作为Web应用中的令牌,用于在各方之间安全地将信息作为JSON对象传输。在数据传输过程
中还可以完成数据加密、签名等相关处理。

http协议无状态的,所以需要sessionId或token的鉴权机制,jwt的token认证机制不需要在服务端再保留用户的认证信息
或会话信息。这就意味着基于jwt认证机制的应用程序不需要去考虑用户在哪一台服务器登录了,这就为应用的扩展提供了便利,
jwt更适用于分布式应用

1、前端通过Web表单将自己的用户名和密码发送到后端的接口,这个过程一般是一个POST请求。建议的方式是通过SSL加密的
传输(HTTPS),从而避免敏感信息被嗅探
2、后端核对用户名和密码成功后,将包含用户信息的数据作为JWT的Payload,将其与JWT Header分别进行Base64编码拼
接后签名,形成一个JWT Token,形成的JWT Token就是一个如同lll.zzz.xxx的字符串
3、后端将JWT Token字符串作为登录成功的结果返回给前端。前端可以将返回的结果保存在浏览器中,退出登录时删除保存的
JWT Token即可
4、前端在每次请求时将JWT Token放入HTTP请求头中的Authorization属性中(解决XSS和XSRF问题)
5、后端检查前端传过来的JWT Token,验证其有效性,比如检查签名是否正确、是否过期、token的接收方是否是自己等
6、验证通过后,后端解析出JWT Token中包含的用户信息,进行其他逻辑操作(一般是根据用户信息得到权限等),返回结果

JWT的优势是:
1、简洁:JWT Token数据量小,传输速度也很快
2、因为JWT Token是以JSON加密形式保存在客户端的,所以JWT是跨语言的,原则上任何web形式都支持
3、不需要在服务端保存会话信息,也就是说不依赖于cookie和session,所以没有了传统session认证的弊端,特别适用于
分布式微服务
4、单点登录友好:使用Session进行身份认证的话,由于cookie无法跨域,难以实现单点登录。但是,使用token进行认证
的话, token可以被保存在客户端的任意位置的内存中,不一定是cookie,所以不依赖cookie,不会存在这些问题
5、适合移动端应用:使用Session进行身份认证的话,需要保存一份信息在服务器端,而且这种方式会依赖到Cookie(需要
Cookie 保存 SessionId),所以不适合移动端

3、jwt的组成部分
标准的jwt令牌分为三部分,分别是Header、payload、signature;在token字符串中使用.进行分割

3.1、Header
它的组成部分包括两点:参数类型jwt,签名的算法hs256

3.2、Payload
它的组成就是登陆用户的一些信息,和token发行和失效时间等;这些内容里面有一些是标准字段,你也可以添加其它需要的内容。
iss:Issuer,发行者
sub:Subject,主题
aud:Audience,观众
exp:Expiration time,过期时间
nbf:Not before
iat:Issued at,发行时间
jti:JWT ID
最后它也会通过Base64加密方式进行编码

5、Signature
是由3个部分组成,先是用Base64编码的header和payload,再用加密算法加密一下,加密的时候要放进去一个Secret,
这个相当于是一个密码,这个密码秘密地存储在服务端。

secret就是在最后第二次加密时加的盐,算是一个秘钥(只保留在服务器),不向外部透露。

JWT每部分的作用,在服务端接收到客户端发送过来的JWT token之后:
header和payload可以直接利用base64解码出原文,从header中获取哈希签名的算法,从payload中获取有效数据。
signature由于使用了不可逆的加密算法,无法解码出原文,它的作用是校验token有没有被篡改。服务端获取header中的
加密算法之后,利用该算法加上secretKey对header、payload进行加密,比对加密后的数据和客户端发送过来的是否一
致。注意secretKey只能保存在服务端,而且对于不同的加密算法其含义有所不同,一般对于MD5类型的摘要加密算法,
secretKey实际上代表的是盐值

// 指定token过期时间为10秒
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.SECOND, 10);

    String token = JWT.create()
            .withHeader(new HashMap<>())  // Header
            .withClaim("userId", 21)  // Payload
            .withClaim("userName", "baobao")
            .withExpiresAt(calendar.getTime())  // 过期时间
            .sign(Algorithm.HMAC256("!34ADAS"));  // 签名用的secret

    System.out.println(token);

解析JWT字符串
// 创建解析对象,使用的算法和secret要与创建token时保持一致
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(“!34ADAS”)).build();
// 解析指定的token
DecodedJWT decodedJWT = jwtVerifier.verify(“eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyTmFtZSI6ImJhb2JhbyIsImV4cCI6MTU5OTkyMjUyOCwidXNlcklkIjoyMX0.YhA3kh9KZOAb7om1C7o3vBhYp0f61mhQWWOoCrrhqvo”);
// 获取解析后的token中的payload信息
Claim userId = decodedJWT.getClaim(“userId”);
Claim userName = decodedJWT.getClaim(“userName”);
System.out.println(userId.asInt());
System.out.println(userName.asString());
// 输出超时时间
System.out.println(decodedJWT.getExpiresAt());

在实际的SpringBoot项目中一般登录流程:
1、在登录验证通过后,给用户生成一个对应的随机token(注意这个token不是指jwt,可以用uuid等算法生成),然后将这个token
作为key的一部分,用户信息作为value存入Redis,并设置过期时间,这个过期时间就是登录失效的时间
2、将第1步中生成的随机token作为JWT的payload生成JWT字符串返回给前端
3、前端之后每次请求都在请求头中的Authorization字段中携带JWT字符串
4、后端定义一个拦截器,每次收到前端请求时,都先从请求头中的Authorization字段中取出JWT字符串并进行验证,验证通过后解析
出payload中的随机token,然后再用这个随机token得到key,从Redis中获取用户信息,如果能获取到就说明用户已经登录

开发流程:
1、依赖Java-jwt
2、定义工具类
//生成jwt token
JWTCreator.Builder builder = JWT.create(); //创建用于生成jwt token的对象
builder.withClaim(“username”,“yanjun”); //添加有效负载,使用token传递的数据
Date nowDate = new Date();
Date expireDate = new Date(nowDate.getTime() + 10 * 1000);
builder.withExpiresAt(expireDate); //过期时间,10s后过期
String token=builder.sign(Algorithm.HMAC256(“zhangsan”)); //指定算法用于生成对应的签名,参数就是生成签名时所使用的盐值
System.out.println(token);

//获取token中传递的数据
String token="eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2NzUxNTc0OTksInVzZXJuYW1lIjoieWFuanVuIn0.cGjegObynlTwixTQNetYSIu1yFjSrjgVfSQNFBKV3vg";
    JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("zhangsan")).build();
    //会针对token进行验证,如果过期或者签名出错则报异常
    DecodedJWT verify = jwtVerifier.verify(token);

    System.out.println(verify.getHeader());
    System.out.println(verify.getToken());
    System.out.println(verify.getPayload());
    System.out.println(verify.getSignature());

    //从payload中获取传递的数据
    String username = verify.getClaim("username").asString();
    System.out.println(username);
}

猜你喜欢

转载自blog.csdn.net/qq_39756007/article/details/128817878
今日推荐