记录一次使用JWT+拦截器的简易登录功能

记录一次使用JWT+拦截器的简易登录功能

Administrator 80 2021-07-17

[toc]

1. 引入依赖

        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.4.0</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

2. 封装工具类

/**
 * @Author 梦叶
 */
@Slf4j
public class JWTUtil {

    /**
     * 签名。不可泄漏
     */
    private static final String SECRET = "51iros.com";

    /**
     * 颁发证书
     * @param map 载体
     * @param second 过期时间,秒
     * @return
     */
    public static String getToken(Map<String, String> map, int second) {
        log.info("开始颁发证书 | playload:[{}] | second:[{}] ", JSON.toJSONString(map), second);
        JWTCreator.Builder builder = JWT.create();
        builder.withIssuedAt(new Date());//签发日期
        //payload
        map.forEach((k, v) -> {
            builder.withClaim(k, v);
        });
        Calendar instance = Calendar.getInstance();
        instance.add(Calendar.SECOND, second); //设置过期时间
        builder.withExpiresAt(instance.getTime());//指定令牌的过期时间
        String token = builder.sign(Algorithm.HMAC256(SECRET));//签名
        return token;
    }

    /**
     * 验证Token
     * @param token
     * @return
     * @throws JWTVerificationException
     */
    public static DecodedJWT verify (String token) throws JWTVerificationException {
        //如果有任何验证异常,此处都会抛出异常
        DecodedJWT decodedJWT = JWT.require(Algorithm.HMAC256(SECRET)).build().verify(token);
        return decodedJWT;
    }



    /**
     * 解析token
     * @param token
     * @return
     * @throws JWTVerificationException
     */
    public static SuperUser getUser(String token) throws JWTVerificationException {
        SuperUser user = new SuperUser();
        //如果有任何验证异常,此处都会抛出异常
        DecodedJWT verify = verify(token);
        user.setSuperUserId(verify.getClaim("superUserId").asInt());
        user.setSuperUserName(verify.getClaim("superUserName").asString());
//        user.setSuperUserName(verify.getClaim("superUserPassword").asString());
        return user;
    }
}

3. 拦截器配置


/**
 * @Author 梦叶
 */
@Slf4j
public class LoginIntercept implements HandlerInterceptor {


    /**
     * 验证token
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //获取请求头中的令牌
        String token = request.getHeader("Authorization");
        log.info("当前token为:{}", token);
        CommResult<Object> result = new CommResult<>();
        try {
            //验证token
            JWTUtil.verify(token);
            return true;
        } catch (SignatureVerificationException e) {
            log.error("签名不一致",e.getMessage());
            result.setCode(CommRespCode.BIZ_FAILED.getKey());
            result.setMessage( "签名不一致");
        } catch (TokenExpiredException e) {
            log.error("令牌过期",e.getMessage());
            result.setCode(CommRespCode.BIZ_FAILED.getKey());
            result.setMessage( "令牌过期");
        } catch (AlgorithmMismatchException e) {
            log.error("算法不匹配",e.getMessage());
            result.setCode(CommRespCode.BIZ_FAILED.getKey());
            result.setMessage( "算法不匹配");
        } catch (InvalidClaimException e) {
            log.error("无效载体",e.getMessage());
            result.setCode(CommRespCode.BIZ_FAILED.getKey());
            result.setMessage( "无效载体");
        } catch (Exception e) {
            log.error("token无效",e.getMessage());
            result.setCode(CommRespCode.BIZ_FAILED.getKey());
            result.setMessage( "token无效");
        }
        String json = JSON.toJSONString(result);
        //响应到前台: 将map转为json
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().println(json);
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("验证通过");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.warn("url:[{}],准备进行登录验证",request.getRequestURI());
    }

}

4. 将拦截器注册到IOC容器,并配置拦截与放行URL


/**
 * @Author 梦叶
 */
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginIntercept())
                .excludePathPatterns("/register")//开通超管账号时放行
                .excludePathPatterns("/supmgr/login")
                .excludePathPatterns("/login")
                .excludePathPatterns("/swagger-resources/**")
                .excludePathPatterns("/doc.html")
                .addPathPatterns("/**");
//                .addPathPatterns("/supmgr/admin/**")
//                .addPathPatterns("/admin/**");

    }
}

5. 登录认证功能实现


/**
 * <p>
 * saas超级管理员用户表 服务实现类
 * </p>
 *
 * @author 梦叶
 * @since 2021-07-17
 */
@Service
public class SuperUserServiceImpl extends ServiceImpl<SuperUserMapper, SuperUser> implements SuperUserService {

    //一小时
    public static final int SECOND = 60 * 60 * 1;

    /**
     * 超管后台登录接口
     *
     * @param superUser
     * @return
     */
    @Override
    public CommResult login(SuperUser superUser) {
        if (BeanUtil.isEmpty(superUser)) {
            return CommResult.error(CommRespCode.ILLEGAL_PARAM.getKey(), "请输入用户名密码");
        }
        if (StrUtil.isBlank(superUser.getSuperUserName())) {
            return CommResult.error(CommRespCode.ILLEGAL_PARAM.getKey(), "用户名不能为空");
        }
        if (StrUtil.isBlank(superUser.getSuperUserPassword())) {
            return CommResult.error(CommRespCode.ILLEGAL_PARAM.getKey(), "密码不能为空");
        }

        String userName = superUser.getSuperUserName();
        SuperUser user = new LambdaQueryChainWrapper<SuperUser>(this.baseMapper)
                .eq(SuperUser::getSuperUserName, userName)
                .eq(SuperUser::getIsEnabled, true)
                .one();
        if (BeanUtil.isEmpty(user)) {
            return CommResult.error(CommRespCode.ILLEGAL_PARAM.getKey(), "用户不存在");
        }
        //校验密码
        //获取salt加盐加密
        String salt = user.getSalt();
        String sha256Hex = DigestUtil.sha256Hex(superUser.getSuperUserPassword() + salt);
        String password = user.getSuperUserPassword();
        if (!sha256Hex.equals(password)) {
            return CommResult.error(CommRespCode.ILLEGAL_PARAM.getKey(), "密码错误");
        }
        //校验通过,颁发token
        HashMap<String, String> userInfo = new HashMap<>();
        userInfo.put("superUserId", user.getSuperUserId().toString());
        userInfo.put("superUserName", user.getSuperUserName());
//        String token = JWTUtil.getToken(userInfo, SECOND);
        String token = JWTUtil.getToken(userInfo, 60);

        //封装数据返回
        HashMap<String, String> map = new HashMap<>();
        map.put(HttpHeaders.AUTHORIZATION, token);
        return CommResult.success(map);
    }

    /**
     * 开通超级管理员账号
     *
     * @param superUser
     * @return
     */
    @Override
    public CommResult register(SuperUser superUser) {
        CheckNullUtil.check(superUser, "superUserName", "superUserPassword");
        String superUserName = superUser.getSuperUserName();
        String password = superUser.getSuperUserPassword();
        if (superUserName.length() > 8) {
            return CommResult.error(CommRespCode.BIZ_FAILED.getKey(), "用户名长度不能超过8");
        }
        //判断用户名是否重复
        Integer count = this.baseMapper.selectCount(new LambdaQueryWrapper<SuperUser>().eq(SuperUser::getSuperUserName, superUser.getSuperUserName()));
        if (count > 0) {
            return CommResult.error(CommRespCode.BIZ_FAILED.getKey(), "用户已存在");
        }


        superUser.setIsEnabled(true);
        //生成盐
        String salt = RandomUtil.randomString(64);
        //加盐加密
        superUser.setSuperUserPassword(DigestUtil.sha256Hex(password + salt));
        superUser.setSalt(salt);
        return this.baseMapper.insert(superUser) > 0 ? CommResult.success() : CommResult.error(CommRespCode.BIZ_FAILED.getKey(), "开通超管失败");
    }
}