使用JWT实现登录认证

一、介绍

1.1、Session、Cookie、Token区别

session:存储再服务端,无法引用与分布式场景,并且需要占用服务端的资源
cookie:存储再客户端,适用于分布式场景,但是存在安全问题,不支持垮域访问
token:存储在localstorage中,更加灵活

1.2、如何实现登录认证

  1. 用户使用账号密码登录成功
  2. 通过JWT生成一串字符串作为Token,返回给前端
  3. 前端每次请求的时候都在请求头中携带上这个Token
  4. 后端每次都使用JWT对该Token进行校验,还原出一些用户信息,以此来判断用户是否登录

1.3、JWT组成

1.3.1、样例

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjpbXSwiaWF0IjoxNjg0ODIzNzIzLCJleHAiOjE2ODQ4NTc1OTksImF1ZCI6IiIsImlzcyI6ImFxaSIsInN1YiI6IiJ9.W306xll5X2lHWL_B0AUZs7nf9e7Zn5QvgoasnviBeaQ

1.3.2、组成

JWT生成的字符串由三个部分组成

第一部分(header:JWT头,该部分只用Base64编码,未加密)
头部由2个属性组成
1、typ:令牌类型,固定设置为JWT
2、alg:加密算法,默认为HS256

第二部分(Payload:有效载荷,该部分只用Base64编码,未加密,避免存放隐私信息)
就是JWT的主体部分
1、issuer:发行者
2、IssuedAt:发布时间
3、expiration:到期时间
4、subject:主题
5、Not Before:生效时间
6、JWT ID:用于标识该 JWT
7、audience:用户

第三部分(Signature:签名,该部分是安全的,无法被解密)
该部分可以设置secret(俗称:加盐)的方式增加该部分的破解难度

1.4、优缺点

1.4.1、优点
  1. 是json格式,跨语言的
  2. 可以利用Payload存储一些非敏感的信息
  3. 不需要存储在服务端,可以用于分布式场景
  4. 一般存储在localStorage中,不存在于Cookie中,避免了一些安全性问题
  5. 便于实现单点登录功能
1.4.2、缺点
  1. 一旦生成就无法修改过期时间,需要搭配缓存来实现过期或者退出效果
  2. 同样Token过期无法进行续签
  3. 不可以在JWT中存储敏感信息

二、使用

2.1、引入POM依赖

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

2.2、编写工具类

package com.xx.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;

/**
 * @author aqi
 * DateTime: 2020/11/9 3:27 下午
 * Description: JWT工具类
 */
public class JwtUtils {

    /**
     * 设置Token过期时间
     */
    public static final long EXPIRE = 1000 * 60 * 60 * 24;

    /**
     * 秘钥
     */
    public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO";

    /**
     * 生成Token字符串(Token组成:头+荷载+签名)
     * @param id 用户id
     * @param nickname 用户名称
     * @return Token字符串
     */
    public static String getJwtToken(String id, String nickname){

        return Jwts.builder()
                // 设置JWT的头信息
                .setHeaderParam("typ", "JWT")
                .setHeaderParam("alg", "HS256")

                // 设置Token过期时间
                // 主题
                .setSubject("guli-user")
                // 签发时间
                .setIssuedAt(new Date())
                // 过期时间
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRE))

                // 设置Token主体部分
                .claim("id", id)
                .claim("nickname", nickname)
                // 签名算法,秘钥
                .signWith(SignatureAlgorithm.HS256, APP_SECRET)
                .compact();
    }

    /**
     * 判断token是否存在与有效
     * @param jwtToken token
     * @return 判断token是否存在与有效
     */
    public static boolean checkToken(String jwtToken) {
        if(StringUtils.isEmpty(jwtToken)) {
            return false;
        }
        try {
            Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 判断token是否存在与有效
     * @param request request
     * @return 判断token是否存在与有效
     */
    public static boolean checkToken(HttpServletRequest request) {
        try {
            String jwtToken = request.getHeader("token");
            if(StringUtils.isEmpty(jwtToken)) {
                return false;
            }
            Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 根据token获取会员id
     * @param request request
     * @return 会员id
     */
    public static String getMemberIdByJwtToken(HttpServletRequest request) {
        String jwtToken = request.getHeader("token");
        if(StringUtils.isEmpty(jwtToken)) {
            return "";
        }
        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        Claims claims = claimsJws.getBody();
        return (String)claims.get("id");
    }

}


2.3、登录代码

public String login(UcenterMember member) {
        //获取登录手机号和密码
        String mobile = member.getMobile();
        String password = member.getPassword();

        //手机号和密码非空判断
        if(StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password)) {
            throw new MyException(20001,"登录失败");
        }

        //判断手机号是否正确
        QueryWrapper<UcenterMember> wrapper = new QueryWrapper<>();
        wrapper.eq("mobile",mobile);
        UcenterMember mobileMember = baseMapper.selectOne(wrapper);
        //判断查询对象是否为空
        if(mobileMember == null) {//没有这个手机号
            throw new MyException(ResultCode.CODE_20001);
        }

        //判断密码
        if(!DigestUtils.md5DigestAsHex(password.getBytes()).equals(mobileMember.getPassword())) {
            throw new MyException(ResultCode.CODE_20001);
        }

        //判断用户是否禁用
        if(mobileMember.getIsDisabled()) {
            throw new MyException(ResultCode.CODE_20001);
        }

        //登录成功,生成token字符串
        return JwtUtils.getJwtToken(mobileMember.getId(), mobileMember.getNickname());
    }

2.4、使用JWT做拦截

/**
     * 根据token获取用户信息
     */
    @GetMapping("/getMemberInfo")
    public R getMemberInfo(HttpServletRequest request) {
        //调用jwt工具类的方法。根据request对象获取头信息,返回用户id
        String memberId = JwtUtils.getMemberIdByJwtToken(request);
        if (memberId.isEmpty()) {
            return R.error().data("userInfo", "登陆失效,请重新登录");
        }
        //查询数据库根据用户id获取用户信息
        UcenterMember member = memberService.getById(memberId);
        return R.success().data("userInfo",member);
    }

三、续签和过期问题

3.1、思路

将生成的JWT字符串不返回给前端,而是自己生成一个UUID,将该UUID返回给前端,再使用该UUID存储

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用JWT实现登录验证是一种基于token的认证机制。在用户登录成功后,服务端会为用户颁发一个token(令牌),用户使用这个token来访问后台的接口。JWT的特点是无状态的,服务端不需要保留用户的认证信息或会话信息,因此可以方便地进行应用扩展。这种鉴权机制不受限于用户登录的服务器,用户可以在任何服务器上使用token进行访问。使用JWT实现登录验证的方法有很多,比如使用Spring Boot等框架来生成和解析token,可以参考相关文档和示例代码来实现具体的功能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [基于JWT实现简单的用户登陆验证(超详细)](https://blog.csdn.net/weixin_47025166/article/details/125373122)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [SpringBoot使用JWT实现登录验证的方法示例](https://download.csdn.net/download/weixin_38668754/12748929)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值