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);
     // 以下逻辑自定义
}

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


相关文章
|
9月前
|
JSON 机器人 API
gewe微信机器人搭建教程
GeWe开放平台是基于 微信开放平台的二次封装API服务,开发者可以使用本服务来处理微信中的各种事件,并可以通过后台调用对应的 API 来驱动微信自动执行任务,如自动收发消息、自动化应答、自动群邀请、群管理等,封装了 RPA技术流程,简化开发者二次开发难度,提供了开发者与微信对接的能力,使用简单,操作快捷,支持多种语言接入。
541 17
|
12月前
|
人工智能 开发框架 机器人
AstrBot:轻松将大模型接入QQ、微信等消息平台,打造多功能AI聊天机器人的开发框架,附详细教程
AstrBot 是一个开源的多平台聊天机器人及开发框架,支持多种大语言模型和消息平台,具备多轮对话、语音转文字等功能。
6457 38
AstrBot:轻松将大模型接入QQ、微信等消息平台,打造多功能AI聊天机器人的开发框架,附详细教程
|
11月前
|
存储 API UED
鸿蒙特效教程02-微信语音录制动画效果实现教程
本教程适合HarmonyOS初学者,通过简单到复杂的步骤,一步步实现类似微信APP中的语音录制动画效果。
435 0
鸿蒙特效教程02-微信语音录制动画效果实现教程
|
存储 小程序 前端开发
微信小程序与Java后端实现微信授权登录功能
微信小程序极大地简化了登录注册流程。对于用户而言,仅仅需要点击授权按钮,便能够完成登录操作,无需经历繁琐的注册步骤以及输入账号密码等一系列复杂操作,这种便捷的登录方式极大地提升了用户的使用体验
3577 12
|
小程序 前端开发 算法
|
移动开发 前端开发 Android开发
开发指南059-App实现微信扫描登录
App是用uniapp开发的,打包为apk,上传到安卓平板中使用
详细教程:扫码提交表单后,数据直接推送到企业微信、钉钉、飞书群聊
在草料制作的表单中,填表人扫码填写并提交数据后,这些信息可以立即通过企业微信、钉钉或飞书自动推送到相应的群聊中,实现即时共享和沟通,提升团队协作效率。
639 2
|
小程序 算法 前端开发
微信小程序---授权登录
微信小程序---授权登录
350 0
|
4月前
|
JavaScript Java 关系型数据库
基于springboot的项目管理系统
本文探讨项目管理系统在现代企业中的应用与实现,分析其研究背景、意义及现状,阐述基于SSM、Java、MySQL和Vue等技术构建系统的关键方法,展现其在提升管理效率、协同水平与风险管控方面的价值。