java springboot 手把手带你敲微信公众号自定义登录实现token拦截【硬货教程】

简介: java springboot 手把手带你敲微信公众号自定义登录实现token拦截【硬货教程】

前言

由于微信官方是无法与我们自己的登录表集成

此篇文章带你写微信公众号根据自己的表进行设计登录系统

项目技术:springboot + mybatisplus

注意:本篇只是基于微信公众号自定义登录逻辑,如果微信公众号还没有集成的话请先集成微信公众号,本项目采用的是java api的形式集成的哦

首先创建表、实体类、以及mapper层

CREATE TABLE `t_sys_user_token` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `open_id` varchar(100) NOT NULL COMMENT '微信用户openid',
  `user_id` varchar(500) DEFAULT NULL COMMENT '用户ID',
  `user_name` varchar(500) DEFAULT NULL COMMENT '用户名',
  `full_name` varchar(500) DEFAULT NULL COMMENT '用户名称',
  `token` varchar(100) NOT NULL COMMENT 'token',
  `type` varchar(10) DEFAULT NULL COMMENT '用户类型',
  `expire_time` datetime DEFAULT NULL COMMENT '过期时间',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  PRIMARY KEY (`id`) USING BTREE,
  UNIQUE KEY `token` (`token`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='系统用户Token';


实体类

package com.cnpc.wechat.entity;
import com.baomidou.mybatisplus.annotations.TableId;
import com.baomidou.mybatisplus.annotations.TableName;
import com.baomidou.mybatisplus.enums.IdType;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.time.LocalDateTime;
/**
 * @author wuzhenyong
 * ClassName:SysUserTokenEntity.java
 * date:2022-06-29 8:30
 * Description:
 */
@ApiModel(value = "系统用户Token", description = "系统用户Token")
@Data
@TableName("t_sys_user_token")
public class SysUserTokenEntity {
    @ApiModelProperty(value = "主键", name = "id", example = "token")
    @TableId(type = IdType.INPUT)
    private Long id;
    /**
     * 微信用户openId
     */
    @ApiModelProperty(value = "微信用户openId", name = "openId", example = "token")
    private String openId;
    /**
     * 用户ID
     */
    @ApiModelProperty(value = "用户ID", name = "userId", example = "token")
    private String userId;
    /**
     * 用户名
     */
    @ApiModelProperty(value = "用户名", name = "userName", example = "token")
    private String userName;
    /**
     * 用户名称
     */
    @ApiModelProperty(value = "用户名称", name = "fullName", example = "token")
    private String fullName;
    @ApiModelProperty(value = "token凭证", name = "token", example = "token")
    private String token;
    /**
     * 用户类型
     */
    @ApiModelProperty(value = "用户类型", name = "type", example = "token")
    private String type;
    /**
     * 过期时间
     */
    @ApiModelProperty(value = "token过期时间", name = "expireTime", example = "token")
    private LocalDateTime expireTime;
    /**
     * 更新时间
     */
    @ApiModelProperty(value = "token更新时间", name = "updateTime", example = "token")
    private LocalDateTime updateTime;
}


持久层就自己创建吧


调用微信登录方法

private final WxMpService wxService;
    @Autowired
    private WxUserTokenService wxUserTokenService;
    @Autowired
    private WxAccountFansService wxAccountFansService;
    @GetMapping("/login")
    @ApiOperation("微信登录方法")
    public R login(@PathVariable String appid, @RequestParam String code) {
        if (!this.wxService.switchover(appid)) {
            throw new IllegalArgumentException(String.format("未找到对应appid=[%s]的配置,请核实!", appid));
        }
        try {
            WxMpOAuth2AccessToken accessToken = wxService.oauth2getAccessToken(code);
            WxMpUser wxMpUser = wxService.oauth2getUserInfo(accessToken, null);
            String type = "1";
            //生成用户token并且返回前端 此方法逻辑自定义
            SysUserTokenEntity tokenEntity = wxUserTokenService.createToken(wxMpUser.getOpenId(), type);
            return R.ok()
                    .put("cnpc-wx-token", tokenEntity.getToken())
                    .put("expire", tokenEntity.getExpireTime().getTime() - System.currentTimeMillis());
        } catch (WxErrorException e) {
            e.printStackTrace();
            return R.error(50000, e.getMessage());
        }
    }

创建微信请求token验证类


代码说明:

1.request.getHeader(“cnpc-wx-token”); cnpc-wx-token是前端传入的header值,也就是说我们调用微信官方登录接口自己生成的一个token

2.生成token完成后保存到我们自己创建的表

3.然后再根据token查询我们自定义的表获取用户openId及其他信息(根据需求自定义)

package com.cnpc.wechat.interceptor;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.cnpc.common.exception.CnpcException;
import com.cnpc.wechat.entity.SysUserTokenEntity;
import com.cnpc.wechat.service.SysUserTokenService;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.LocalDateTime;
/**
 * @author wuzhenyong
 * ClassName:ServiceAuthorizationInterceptor.java
 * date:2022-06-29 8:24
 * Description: 微信客服中心相关token验证
 */
@Component
public class ServiceAuthorizationInterceptor extends HandlerInterceptorAdapter {
    @Autowired
    private SysUserTokenService sysUserTokenService;
    public static final String USER_KEY = "sysUserOpenId";
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        //从header中获取token
        String token = request.getHeader("cnpc-wx-token");
        //如果header中不存在token,则从参数中获取token
        if (StringUtils.isBlank(token)) {
            token = request.getParameter("cnpc-wx-token");
        }
        //token为空
        if (StringUtils.isBlank(token)) {
            throw new CnpcException("token不能为空", 401);
        }
        //查询token信息
        SysUserTokenEntity sysUserTokenEntity = sysUserTokenService.selectOne(new EntityWrapper<SysUserTokenEntity>().eq("token", token));
        //正常token
        if (sysUserTokenEntity == null || sysUserTokenEntity.getExpireTime().isBefore(LocalDateTime.now())) {
            throw new CnpcException("token失效,请重新登录", 401);
        }
        //设置userId到request里,后续根据openId,获取用户信息
        request.setAttribute(USER_KEY, sysUserTokenEntity.getOpenId());
        return true;
    }
}

创建微信方法注解

import java.lang.annotation.*;
/**
 * @author wuzhenyong
 * ClassName:SysUserLogin.java
 * date:2022-06-29 8:33
 * Description: 系统用户登录注解
 */
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SysUserLogin {
}

创建微信方法注解实体解析

1.此解析类是为了解析微信请求方法中加入SysUserLogin注解的实体类进行解析,是为了让注解上的实体类赋值,查询我们数据库中的数据进行赋值

package com.cnpc.wechat.resolver;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.cnpc.wechat.annotation.SysUserLogin;
import com.cnpc.wechat.entity.SysUserTokenEntity;
import com.cnpc.wechat.interceptor.ServiceAuthorizationInterceptor;
import com.cnpc.wechat.service.SysUserTokenService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
/**
 * @author wuzhenyong
 * ClassName:SysLoginUserHandlerMethodArgumentResolver.java
 * date:2022-06-29 8:45
 * Description: 系统用户方法注解实体解析
 */
@Component
public class SysLoginUserHandlerMethodArgumentResolver  implements HandlerMethodArgumentResolver {
    @Autowired
    private SysUserTokenService sysUserTokenService;
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.getParameterType().isAssignableFrom(SysUserTokenEntity.class) && parameter.hasParameterAnnotation(SysUserLogin.class);
    }
    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer container,
                                  NativeWebRequest request, WebDataBinderFactory factory) throws Exception {
        //获取用户ID
        Object object = request.getAttribute(ServiceAuthorizationInterceptor.USER_KEY, RequestAttributes.SCOPE_REQUEST);
        if (object == null) {
            return null;
        }
        //获取用户信息
        SysUserTokenEntity sysUserTokenEntity = sysUserTokenService.selectOne(new EntityWrapper<SysUserTokenEntity>()
                .eq("open_id", object)
                .orderBy("expire_time", Boolean.FALSE)
                .last("limit 1"));
        return sysUserTokenEntity;
    }
}

WebMvc配置类 配置微信请求拦截

1.SysLoginUserHandlerMethodArgumentResolver 就是我们自定义的注解解析器

2./service/wx/** 此路径就是本项目定义的路径

3.registry.addInterceptor(serviceAuthorizationInterceptor()).addPathPatterns(“/service/wx/**”); 添加我们自定义的token拦截校验

/**
 * WebMvc配置
 * @author YangMQ
 */
@Configuration
public class WebAppConfig implements WebMvcConfigurer {
    private final Logger logger = LoggerFactory.getLogger(WebAppConfig.class);
    @Autowired
    private SysLoginUserHandlerMethodArgumentResolver sysLoginUserHandlerMethodArgumentResolver;
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        argumentResolvers.add(sysLoginUserHandlerMethodArgumentResolver);
    }
    @Bean
    public MultipartConfigElement multipartConfigElement() {
        MultipartConfigFactory factory = new MultipartConfigFactory();
        //文件最大KB,MB
        factory.setMaxFileSize("1024MB");
        //设置总上传数据总大小
        factory.setMaxRequestSize("1024MB");
        return factory.createMultipartConfig();
    }
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/statics/**").addResourceLocations("classpath:/statics/");
        // TODO Auto-generated method stub
        WebMvcConfigurer.super.addResourceHandlers(registry);
    }
    /**
     * 解决跨域问题
     * @param registry
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/service/wx/**");
    }
    /**
     * 统一异常处理
     * @param exceptionResolvers
     */
    @Override
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {
        exceptionResolvers.add(new HandlerExceptionResolver() {
            @Override
            public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception e) {
                R r = new R();
                //业务失败的异常,如“账号或密码错误”
                if (e instanceof CnpcException) {
                    r = R.error(((CnpcException) e).getCode(), e.getMessage());
                    logger.error(e.getMessage());
                } else if (e instanceof NoHandlerFoundException) {
                    r = R.error(ResultCode.NOT_FOUND.getCode(), "接口 [" + request.getRequestURI() + "] 不存在");
                } else if (e instanceof ServletException) {
                    r = R.error(ResultCode.FAILURE.getCode(), e.getMessage());
                } else {
                    r = R.error(ResultCode.INTERNAL_SERVER_ERROR.getCode(), "接口 [" + request.getRequestURI() + "] 内部错误,请联系管理员");
                    String message;
                    if (handler instanceof HandlerMethod) {
                        HandlerMethod handlerMethod = (HandlerMethod) handler;
                        message = String.format("接口 [%s] 出现异常,方法:%s.%s,异常摘要:%s",
                                request.getRequestURI(),
                                handlerMethod.getBean().getClass().getName(),
                                handlerMethod.getMethod().getName(),
                                e.getMessage());
                    } else {
                        message = e.getMessage();
                    }
                    logger.error(message, e);
                }
                responseResult(response, r);
                return new ModelAndView();
            }
        });
    }
    /**
     * 设置微信请求拦截路径
     *
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(serviceAuthorizationInterceptor()).addPathPatterns("/service/wx/**");
    }
    /**
     * 系统用户请求登录拦截 我们自定义的拦截器
     *
     * @return
     */
    @Bean
    public ServiceAuthorizationInterceptor serviceAuthorizationInterceptor() {
        return new ServiceAuthorizationInterceptor();
    }
    private void responseResult(HttpServletResponse response, R r) {
        response.setCharacterEncoding("UTF-8");
        response.setHeader("Content-type", "application/json;charset=UTF-8");
        response.setHeader("Access-Control-Allow-Origin", "*");
        response.setHeader("Access-Control-Allow-Methods", "POST, GET, PUT");
        response.setHeader("Access-Control-Max-Age", "3628800");
        response.setStatus(200);
        try {
            response.getWriter().write(JSON.toJSONString(r));
        } catch (IOException ex) {
            logger.error(ex.getMessage());
        }
    }
}

登录方法

1.自己自定义就可以,账号密码,然后进行数据库查验

2.以上写的拦截器如果没有登录时可以拦截的

3.因为我们在拦截器中是判断了请求头中没有拦截器是无法请求的

16a7cab7a33e4d9ba1d019596a79c900.png


自定义的登录


开发微信请求方法(项目业务代码)

@SysUserLogin SysUserTokenEntity sysUserTokenEntity 实体类就已经有我们在拦截器查询数据库赋值后的信息了哦


//类路径@RequestMapping("/service/wx/workOrderService")
@RequestMapping("captcha.jpg")
public voidcaptcha(@SysUserLogin SysUserTokenEntity sysUserTokenEntity) throws IOException {
     System.out.println(sysUserTokenEntity);
     // 以下逻辑自定义
}

以上就完成啦!光看没有用,重要的还是实践啊


相关文章
|
4月前
|
存储 Java 索引
用Java语言实现一个自定义的ArrayList类
自定义MyArrayList类模拟Java ArrayList核心功能,支持泛型、动态扩容(1.5倍)、增删改查及越界检查,底层用Object数组实现,适合学习动态数组原理。
200 4
|
4月前
|
Oracle Java 关系型数据库
Java 简单教程
Java是跨平台、面向对象的编程语言,广泛用于企业开发、Android应用等。本教程涵盖环境搭建、基础语法、流程控制、面向对象、集合与异常处理,助你快速入门并编写简单程序,为进一步深入学习打下坚实基础。
411 0
|
5月前
|
安全 Java
Java之泛型使用教程
Java之泛型使用教程
409 10
|
6月前
|
Java 数据库 C++
Java异常处理机制:try-catch、throws与自定义异常
本文深入解析Java异常处理机制,涵盖异常分类、try-catch-finally使用、throw与throws区别、自定义异常及最佳实践,助你写出更健壮、清晰的代码,提升Java编程能力。
|
6月前
|
Java 关系型数据库 数据库
Java 项目实战教程从基础到进阶实战案例分析详解
本文介绍了多个Java项目实战案例,涵盖企业级管理系统、电商平台、在线书店及新手小项目,结合Spring Boot、Spring Cloud、MyBatis等主流技术,通过实际应用场景帮助开发者掌握Java项目开发的核心技能,适合从基础到进阶的学习与实践。
976 3
|
7月前
|
缓存 安全 Java
Java 并发新特性实战教程之核心特性详解与项目实战
本教程深入解析Java 8至Java 19并发编程新特性,涵盖CompletableFuture异步编程、StampedLock读写锁、Flow API响应式流、VarHandle内存访问及结构化并发等核心技术。结合电商订单处理、缓存系统、实时数据流、高性能计数器与用户资料聚合等实战案例,帮助开发者高效构建高并发、低延迟、易维护的Java应用。适合中高级Java开发者提升并发编程能力。
279 0
|
7月前
|
XML 人工智能 Java
java通过自定义TraceId实现简单的链路追踪
本文介绍了如何在Spring Boot项目中通过SLF4J的MDC实现日志上下文traceId追踪。内容涵盖依赖配置、拦截器实现、网关与服务间调用的traceId传递、多线程环境下的上下文同步,以及logback日志格式配置。适用于小型微服务架构的链路追踪,便于排查复杂调用场景中的问题。
368 0
|
8月前
|
Oracle Java 关系型数据库
java 编程基础入门级超级完整版教程详解
这份文档是针对Java编程入门学习者的超级完整版教程,涵盖了从环境搭建到实际项目应用的全方位内容。首先介绍了Java的基本概念与开发环境配置方法,随后深入讲解了基础语法、控制流程、面向对象编程的核心思想,并配以具体代码示例。接着探讨了常用类库与API的应用,如字符串操作、集合框架及文件处理等。最后通过一个学生成绩管理系统的实例,帮助读者将理论知识应用于实践。此外,还提供了进阶学习建议,引导学员逐步掌握更复杂的Java技术。适合初学者系统性学习Java编程。资源地址:[点击访问](https://pan.quark.cn/s/14fcf913bae6)。
1046 2
|
11月前
|
JSON Java 数据格式
微服务——SpringBoot使用归纳——Spring Boot中的全局异常处理——拦截自定义异常
本文介绍了在实际项目中如何拦截自定义异常。首先,通过定义异常信息枚举类 `BusinessMsgEnum`,统一管理业务异常的代码和消息。接着,创建自定义业务异常类 `BusinessErrorException`,并在其构造方法中传入枚举类以实现异常信息的封装。最后,利用 `GlobalExceptionHandler` 拦截并处理自定义异常,返回标准的 JSON 响应格式。文章还提供了示例代码和测试方法,展示了全局异常处理在 Spring Boot 项目中的应用价值。
550 0
|
Java Maven 开发者
编写SpringBoot的自定义starter包
通过本文的介绍,我们详细讲解了如何创建一个Spring Boot自定义Starter包,包括自动配置类、配置属性类、`spring.factories`文件的创建和配置。通过自定义Starter,可以有效地复用公共配置和组件,提高开发效率。希望本文能帮助您更好地理解和应用Spring Boot自定义Starter,在实际项目中灵活使用这一强大的功能。
1031 17