使用Jwt验证登录demo

一、准备

  1. 依赖
<!--jwt依赖-->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.4.0</version>
        </dependency>

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-api</artifactId>
            <version>0.10.7</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-impl</artifactId>
            <version>0.10.7</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt-jackson</artifactId>
            <version>0.10.7</version>
        </dependency>
  1. 使用Md5工具对数据库密码加密
@Slf4j
public class Md5Utils {
    
    

    public static String getMD5String(String str) {
    
    
        try {
    
    
            // 生成一个MD5加密计算摘要
            MessageDigest md = MessageDigest.getInstance("MD5");
            // 计算md5函数
            md.update(str.getBytes());
            // digest()最后确定返回md5 hash值,返回值为8位字符串。因为md5 hash值是16位的hex值,实际上就是8位的字符
            // BigInteger函数则将8位的字符串转换成16位hex值,用字符串来表示;得到字符串形式的hash值
            //一个byte是八位二进制,也就是2位十六进制字符(2的8次方等于16的2次方)
            return new BigInteger(1, md.digest()).toString(16);
        } catch (Exception e) {
    
    
            StackTraceElement stackTraceElement = e.getStackTrace()[0];
            log.error("{}计算md5异常:{}",stackTraceElement,e.getMessage());
            return null;
        }
    }

}
  1. 生成rsa的私钥和公钥,可以在线生成,不要空格不要换行,不要其他的字符
  2. 从数据库获取用户名和使用md5加密的密码
使用redis存储token,并且一个用户只允许有一个token
@Override
    public ResultVo login(ManagerDto managerDto) {
    
    
        if (ObjectUtils.isEmpty(managerDto)){
    
    
            return new ResultVo().failure(ResultCode.PARAMETER_INVALID);
        }
        try {
    
    
            String userName = managerDto.getUserName();
            String password = managerDto.getPassword();
            IdleManagerEntity manager = managerMapper.getManager(userName);
            if (manager == null){
    
    
                log.info("用户名为空");
                return new ResultVo().failure(ResultCode.USER_NULL);
            }

            String md5String = Md5Utils.getMD5String(password);
            if (!md5String.equals(manager.getMd5Password())){
    
    
                log.info("密码错误");
                return new ResultVo().failure(ResultCode.PASSWORD_ERROR);
            }

            manager.setMd5Password("");
            try {
    
    
                String effective = tokenEffectiveUtils.isEffective(userName);
                // 获取私钥
                ClassPathResource classPathResource = new ClassPathResource("rsa_pri");
                InputStream inputStream = classPathResource.getInputStream();
                PrivateKey privateKey = RsaUtils.getPrivateKey(ResourceStream.readBytes3(inputStream));
                // 生成token
                String token = JwtUtils.generateTokenExpireInMinutes(manager, privateKey, timeOut);
                if (StringUtils.isBlank(effective)){
    
    
                    redisUtils.set(userName,token,timeOut);
                    log.info("{} create new token : {},timeout:{} min",userName,token,timeOut);
                }else {
    
    
                    redisUtils.delete(userName);
                    redisUtils.set(userName,token,timeOut);
                    log.info("{} update token:{},timeOut:{} min",userName,token,timeOut);
                }
                log.info(" {} already login",userName);
                return new ResultVo().success(token);
            } catch (Exception e) {
    
    
                StackTraceElement stackTraceElement = e.getStackTrace()[0];
                log.error("{} token error:{}",stackTraceElement,e.getMessage());
                return new ResultVo().failure(ResultCode.SYSTEM_ERROR);
            }
        } catch (Exception e) {
    
    
            StackTraceElement stackTraceElement = e.getStackTrace()[0];
            log.error("登录异常");
            return new ResultVo().failure(ResultCode.SYSTEM_ERROR);
        }
    }

二、需要对请求方法进行拦截,判断是否携带token或者是否过期

  • 后端登录拦截
从redis中获取token,判断当前携带的token是否与redis的token相同
package com.idle.idlemanager.config;

import com.idle.idlemanager.entity.dataobject.IdleManagerEntity;
import com.idle.idlemanager.enums.ResultCode;
import com.idle.idlemanager.util.JwtUtils;
import com.idle.idlemanager.util.RsaUtils;
import com.idle.idlemanager.util.TokenEffectiveUtils;
import com.idle.idlemanager.vo.ResultVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.security.PublicKey;

/**
 * @Description:
 * @date: 2022/3/11 17:37
 * @author: towards
 * @since JDK 1.8
 */
@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {
    
    

    @Autowired
    private TokenEffectiveUtils tokenEffectiveUtils;

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response, Object handler) throws Exception {
    
    
        //放行请求方法为OPTIONS的请求
        String method = request.getMethod();
        if(method.equals("OPTIONS")){
    
    
            return  true;
        }
        //获取请求头中的请求信息
        String token = request.getHeader("token");
        //使用公钥解密进行校验
        //获取公钥
        PublicKey publicKey = RsaUtils.getPublicKey(
                ResourceUtils.getFile("src/main/resources/rsa_pub").getPath());
        try {
    
    
            IdleManagerEntity manager = (IdleManagerEntity) JwtUtils.getInfoFromToken(token, publicKey, IdleManagerEntity.class);
            if (!token.equals(tokenEffectiveUtils.isEffective(manager.getUserName()))){
    
    
                response.sendRedirect(request.getContextPath()+"/result");
                return false;
            }
            return true;
        }catch (Exception e){
    
    
            StackTraceElement stackTraceElement = e.getStackTrace()[0];
            log.error("{}token error:{}",stackTraceElement,e.getMessage());
        }
        //失败要响应json数据给前端
        response.sendRedirect(request.getContextPath()+"/result");
        return false;
    }

}

  • 后端响应错误信息
@RequestMapping("result")
    public ResultVo result(){
    
    
        return new ResultVo().failure(ResultCode.TOKEN_OVERDUE);
    }

三、需要将拦截器注册到springmvc中,放行登录请求

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    
    

    @Autowired
    private LoginInterceptor loginInterceptor;

    /**
     * 注册拦截器
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
    
    
        /**
         * addInterceptor 注册拦截器
         * addPathPatterns 配置拦截规则
         */
        registry.addInterceptor(loginInterceptor)
                .excludePathPatterns("/login")
                .addPathPatterns("/**");
    }
}

四、前端配置拦截器

  • 配置请求拦截器,为每一次请求添加token
//请求拦截器
service.interceptors.request.use(
  config => {
    
    
  	config.headers.token = sessionStorage.getItem("token");
    return config
  }
)
  • 配置响应拦截器,根据后端“result”返回的错误信息验证token是否过期
// response interceptor  axios响应拦截器
service.interceptors.response.use(
  response => {
    
    
      if(response.data.code == 'A0004'){
    
    
          sessionStorage.removeItem("token")
          location.href = '/login'
          return ;
      }
      return response.data
  }
)

五、贴上公钥私钥吧

  • 私钥,rsa_pri
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDXmLKFi9QRf7srERtx6VUQMJsswm+AFnxyZr8eYpm4/8VdIT345eqXeo/H9Jk6ZTkOjqDjGSt/CoHW2X/ZlnGSVqF03aMYgUMBw3MTcbMOEyyROLVWNHVhNXX1SIJPtl6/J0g/7phC0p3GnAqoxet/lEqK+G6lO4ShEiiDeN3PUjsFDt72MVguKVLvILJ0jB/i+Xk+xY89pFafJp51yAN3MTiKP++A0OYjrt9JrrIHc3LLnPOmyrfu88ppUbQc+JX5UVlzY96FrvNHgV4BpgISP9cAkT5URXB6UlPU7ji1h9c2mHTZp0yWGC9rM+dT/Wu1uwYQArhjaS/2G1LZ3wDfAgMBAAECggEBAKfKal3vJjv8OfHZnY949j/kZFBgfqCkX3Q1rNZ8DIFjZfoGDhIw9fOwakhHgB1nfBFtRZ+ZDjYQOl645Xy1TmyCyaOHfxBqQXkU4PGoEp+Ioq3fAZo2t4fRMJ2WMfzRxUllzCAdlBiljC9vcAhMCO1AvwcLNCmaea1V0XwIzDA1s9C+xcmTUupSMATxyKYM9cRf5EW6tYuQTHFTRmvl6f2Ge9SfQRGZx0QC1HCQ1WwejT3QKIB4sk7i933fP54EcbUwNL+S9kr/Z7xHKiXkyPeFfBBZfi1mKOr+tQhwsnisauLk60j//KgW07ivS5fVYvm/42j4D/+WMO3unoSrhkECgYEA+qGakiCEY+WW+CwSVAHy6dmZQLAa/lkSJKaMRYbUb1xcEcEAUdrRjHiw1RePmFVlzpfPkay8u19GS4WMbOIQREGqMgEeVef3pVTVm/PGxEqVHom3l9kqKI0N5tJihJpWoKrfybP2nIPfA7neEYRdI2ZH5qeIx2yRnG4Rac3cWmECgYEA3Db40TJxgBZ0jzIrmOf3qq8osjuLd1bTtvbps21ceoZzO1bAg2YwsaEGHkucuVBNFhRJBaRXmmEfwQHhsGJfW+SaxbH3NLygETu4OneuK43TBrFqW9tyYZBOG4itjEHyBQP9ijotRiRx/4B5X8kvyjwIC0g8dlgOcAplv3O5oz8CgYBmHKNKN99Yh/jbQbFx8p1Sc2pB9b35tLZ0ojpyNT2l5V5JiXWk498OOTnqh7/kU5637hgOhO8b8/RC3rVYwz3XYV2DI/uFCo5WGNsyv75SyQQ6PefEASFxBg8M5NhoqCz1Jskod57ZI80fyoFUZ7Y+rvRKYCWVAoHM1vdXQR0u4QKBgQCaAZPncbiw1IN1taeJywiJBPCYHU5/CmkL9wgfpKbdRXQHFreR7YC2aR+HRmf3rbILc+pRmMpvO+diKre0jEeWU3zuL3TsG9Jx2usPcK2M5iWj86WnUgRfOPV3ChIdGe1xFoZVCjSpbkmPX0EC/QeADpWtljo6pYCLWKopx3wXLwKBgQDIEfOiQVVsq8F/T5Ja7ut0D96llXRjDFRT2gQ9gAh/bGmfesEweeAt3yrYtEwOk0i7whhfLiXYLZjCIe+EjVDbvxHGvQ1Xj7uvvIqzzboQ6lmcM6+VIqIIGxm4hS/uz5ul6gI48CB7k/1dKSZowiQkTA1I5DMyl4ZqWflP34dIxg==
  • 公钥,rsa_pub
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA15iyhYvUEX+7KxEbcelVEDCbLMJvgBZ8cma/HmKZuP/FXSE9+OXql3qPx/SZOmU5Do6g4xkrfwqB1tl/2ZZxklahdN2jGIFDAcNzE3GzDhMskTi1VjR1YTV19UiCT7ZevydIP+6YQtKdxpwKqMXrf5RKivhupTuEoRIog3jdz1I7BQ7e9jFYLilS7yCydIwf4vl5PsWPPaRWnyaedcgDdzE4ij/vgNDmI67fSa6yB3Nyy5zzpsq37vPKaVG0HPiV+VFZc2Peha7zR4FeAaYCEj/XAJE+VEVwelJT1O44tYfXNph02adMlhgvazPnU/1rtbsGEAK4Y2kv9htS2d8A3wIDAQAB

六、redis工具类

package com.idle.idlemanager.util;
 
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * redis操作工具类.</br>
 * (基于RedisTemplate)
 * @author xcbeyond
 * 2018年7月19日下午2:56:24
 */
@Slf4j
@Component
public class RedisUtils {
    
    
 
	@Autowired
	private RedisTemplate<String, String> redisTemplate;
 
	/**
	 * 读取缓存
	 * 
	 * @param key
	 * @return
	 */
	public String get(final String key) {
    
    
		return redisTemplate.opsForValue().get(key);
	}
 
	/**
	 * 写入缓存
	 */
	public boolean set(final String key, String value,Integer timeOut) {
    
    
		boolean result = false;
		try {
    
    
			redisTemplate.opsForValue().set(key, value,timeOut, TimeUnit.MINUTES);
			result = true;
		} catch (Exception e) {
    
    
			StackTraceElement stackTraceElement = e.getStackTrace()[0];
			log.error("{} redis set error:{}",stackTraceElement,e.getMessage());
		}
		return result;
	}
 
	/**
	 * 更新缓存
	 */
	public boolean getAndSet(final String key, String value) {
    
    
		boolean result = false;
		try {
    
    
			redisTemplate.opsForValue().getAndSet(key, value);
			result = true;
		} catch (Exception e) {
    
    
			StackTraceElement stackTraceElement = e.getStackTrace()[0];
			log.error("redis update error",stackTraceElement,e.getMessage());
		}
		return result;
	}
 
	/**
	 * 删除缓存
	 */
	public boolean delete(final String key) {
    
    
		boolean result = false;
		try {
    
    
			redisTemplate.delete(key);
			result = true;
		} catch (Exception e) {
    
    
			StackTraceElement stackTraceElement = e.getStackTrace()[0];
			log.error("redis delete error",stackTraceElement,e.getMessage());
		}
		return result;
	}
}

七、判断当前用户是否过期

package com.idle.idlemanager.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

/**
 * author:towards
 * date:2022/3/26 13:13
 * discribe: 判断用户token是否过期!
 */
@Slf4j
@Component
public class TokenEffectiveUtils {
    
    

    @Autowired
    private RedisUtils redisUtils;

    /**
     *  判断当前用户名的token是否过期
     * @param managerName 管理员名字
     * @return
     */
    public String isEffective(String managerName){
    
    
        String s = "";
        try {
    
    
            s = redisUtils.get(managerName);
        } catch (Exception e) {
    
    
            StackTraceElement stackTraceElement = e.getStackTrace()[0];
            log.error("{} judge managermanent token effective error:{}",stackTraceElement,e.getMessage());
        }
        return s;
    }
}

猜你喜欢

转载自blog.csdn.net/Proxbj/article/details/123431353
今日推荐