登录使用cookie还是token呢?(上)

最开始是将登陆的用户信息存放在Session,用户在客户端的标识就存在COOKIE中,每次请求接口,在request的header携带cookie就OK。毕竟Cookie目的就是为每一个用户提供不同的身份认证。而使用Session存储用户信息其实也是利用Cookie(Session是利用Cookie进行信息处理的!),当用户首先进行了请求后,服务端就在用户浏览器上创建了一个Cookie,当这个Session结束时,其实就是意味着这个Cookie就过期了(而且Cookie数据保存在客户端,Session数据保存在服务器端。只不过Cookie和Session都是用来跟踪浏览器用户身份的会话方式而已啦!)。故事重点是Cookie跨域请求会失效(因为cookie 无法被自动携带至其他域名),然后老是被前端凶,这样的日子太悲伤了......那就换Token咯!谁让我这么喜新厌旧嘞。开工,因为是Java的项目,然后网上比较多的是JWT(json web token),那就先来个maven吧:

        <!--jwt工具类-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.0</version>
        </dependency>

我找好几个maven尝试,可就这个比较简单,我这个人真的是怕麻烦!所以你不要老是哭!我不会哄你的(委屈就抬头看看天空吧,不是它这么大可以包容你所以的委屈,而是你抬头了眼泪就不会流出来了呀!)继续正题,来串代码(BUG):



import com.alibaba.fastjson.JSONObject;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

public class JwtDemo {

    //HS256加密算法
    private final static SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS256;

    //服务器端自定义的secret
    private final static String secret = "secretKeyzbyjsonwebtoken";

    // 过期时间(单位秒)/ 2小时
    private final static Long access_token_expiration = 7200L;

    //jwt签发者  乱写的
    private final static String jwt_iss = "zby";

    //jwt所有人
    private final static String subject = "zby";

    /**
     * 创建jwt
     * @param user_id   用户id
     * @param user_phone  用户手机号
     * @return  返回一个token
     */
    public static String generateJwtToken(String user_id,String user_phone){

        // Jwt的头部承载
        // 可不设置 默认格式是{"alg":"HS256"}
        Map<String, Object> map = new HashMap<>();
        map.put("alg", "HS256");
        map.put("typ", "JWT");

        //Jwt的载荷
        Map<String,Object> claims = new HashMap<>();
        //私有声明, 自定义数据
        //我一般会携带用户的id或者phone
        claims.put("id",user_id);
        claims.put("phone", user_phone);

        //标准中注册的声明 一旦写标准声明赋值之后,就会覆盖了那些标准的声明
        /*	iss: jwt签发者
            sub: jwt所面向的用户
            aud: 接收jwt的一方
            exp: jwt的过期时间,这个过期时间必须要大于签发时间
            nbf: 定义在什么时间之前,该jwt都是不可用的.
            iat: jwt的签发时间
            jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击
       */
        claims.put("iss", jwt_iss);


        //在payload添加各种标准声明和私有声明
        // 这里其实就是new一个JwtBuilder,设置jwt的body
        return Jwts.builder()
                .setHeader(map)         // 头部信息
                .setClaims(claims)      // 载荷信息
                .setId(UUID.randomUUID().toString()) // 设置jti(JWT ID):是JWT的唯一标识,从而回避重放攻击。
                .setIssuedAt(new Date())       // 设置iat: jwt的签发时间
                .setExpiration(new Date(System.currentTimeMillis() + access_token_expiration * 1000)) // 设置exp:jwt过期时间   7200秒  2小时
                .setSubject(subject)    //设置sub:代表这个jwt所面向的用户,所有人
                .signWith(SIGNATURE_ALGORITHM, secret)//设置签名:通过签名算法和秘钥生成签名
                .compact(); // 开始压缩为xxxxx.yyyyy.zzzzz 格式的jwt token
    }

    /**
     *
     * @param jwt  token
     * @return 返回载荷信息
     */
    private static Claims getClaimsFromJwt(String jwt) {
        Claims claims = null;
        try {
            claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(jwt).getBody();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return claims;
    }

    /**
     *
     * @param jwt token
     * @return 返回jsonObject 包含获取token的状态
     */
    public static JSONObject validateJWT(String jwt){
        JSONObject jsonObject = new JSONObject();
        Claims claims;
        try {
            claims = getClaimsFromJwt(jwt);
            jsonObject.put("Success", true);
            jsonObject.put("Claims", claims);
        } catch (ExpiredJwtException e) {
            // token失效  过期
            jsonObject.put("Success", false);
            jsonObject.put("ErrCode", 1005);
            e.printStackTrace();
        } catch (Exception e) {
            // 未登录
            jsonObject.put("Success", false);
            jsonObject.put("ErrCode", 1004);
            e.printStackTrace();
        }
        return jsonObject;
    }

    public static void main(String[] args) {
        String jwtToken = generateJwtToken("1","15842961234");
        System.out.println("JWT Token "+ jwtToken);

        Claims claims = getClaimsFromJwt(jwtToken);
        System.out.println(claims.get("phone"));

        System.out.println(validateJWT(jwtToken));
    }


}

这个一个Demo,嘻嘻嘻!签名算法备注:JWT的签名算法有HS256和RS256,都是用来加密的,只是RS256是一种非对称算法 (采用SHA-256 的 RSA 签名) ,HS256  是一种对称算法(采用SHA-256 的 HMAC 签名)

对称算法 
当使用某个密钥加密数据时,也可以使用该密钥对数据进行解密。(这里只使用了一个密钥)


不对称算法
首先使用公钥来加密消息,然后再使用公钥对应的私钥来解密它。(这里使用了两个密钥)

所以这就是网上大部分说使用HS256安全性高一点,因为你可以控制谁使用这个密钥,但是如果你无法控制客户端,或者你无法保护密钥,RS256将更适合,因为用户只需要知道公共密钥就好了的原因。好像是乱说的.....

我现在使用的是HS256哦!会在用户登录分配一个token给用户,然后存Redis中,生成token时会设置过期时间,但是一般我也会在存Redis时设置相同的过期时间。下面是Interceptor的一段判断请求接口是否携带token

String token = request.getHeader("TOKEN");

//判断token
if (TextUtils.isEmpty(token)){
	response.setCharacterEncoding("UTF-8");
	response.setContentType("application/json;charset=utf-8");
	response.getWriter().write("处于未登录状态,请先登录");
	response.getWriter().close();
	return false;
}else {
	JSONObject jsonObject = JwtDemo.validateJWT(token);
	if (jsonObject.getBooleanValue("Success")){
		Claims claims = (Claims) jsonObject.get("Claims");
		String id =  claims.get("id").toString();
        //这里已经拿到用户id,然后就可以继续你代码里面的正常操作
    }else {
		int RtnValue = jsonObject.getIntValue("ErrCode");
		String msg = "会话已过期,请重新登录";
		switch (RtnValue) {
			case 1005:
			    msg = "签名已过期,请重新登录";
			    break;
			case 1004:
			    msg = "正处于未登录状态,请先登录";
			    break;
			default:
				break;
		}
		response.setCharacterEncoding("UTF-8");
		response.setContentType("application/json;charset=utf-8");
		response.getWriter().write(msg);
		response.getWriter().close();
		return false;
}

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ZBY52031

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值