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

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


相关文章
|
7月前
|
Java 关系型数据库 数据库
Java 项目实战教程从基础到进阶实战案例分析详解
本文介绍了多个Java项目实战案例,涵盖企业级管理系统、电商平台、在线书店及新手小项目,结合Spring Boot、Spring Cloud、MyBatis等主流技术,通过实际应用场景帮助开发者掌握Java项目开发的核心技能,适合从基础到进阶的学习与实践。
1092 3
|
11月前
|
前端开发 Java 关系型数据库
基于Java+Springboot+Vue开发的鲜花商城管理系统源码+运行
基于Java+Springboot+Vue开发的鲜花商城管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的鲜花商城管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。技术学习共同进步
682 7
|
6月前
|
安全 Java
Java之泛型使用教程
Java之泛型使用教程
434 10
|
5月前
|
Oracle Java 关系型数据库
Java 简单教程
Java是跨平台、面向对象的编程语言,广泛用于企业开发、Android应用等。本教程涵盖环境搭建、基础语法、流程控制、面向对象、集合与异常处理,助你快速入门并编写简单程序,为进一步深入学习打下坚实基础。
449 0
|
11月前
|
存储 Java 数据库
Spring Boot 注册登录系统:问题总结与优化实践
在Spring Boot开发中,注册登录模块常面临数据库设计、密码加密、权限配置及用户体验等问题。本文以便利店销售系统为例,详细解析四大类问题:数据库字段约束(如默认值缺失)、密码加密(明文存储风险)、Spring Security配置(路径权限不当)以及表单交互(数据丢失与提示不足)。通过优化数据库结构、引入BCrypt加密、完善安全配置和改进用户交互,提供了一套全面的解决方案,助力开发者构建更 robust 的系统。
380 0
|
JavaScript NoSQL Java
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
670 96
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
|
8月前
|
缓存 安全 Java
Java 并发新特性实战教程之核心特性详解与项目实战
本教程深入解析Java 8至Java 19并发编程新特性,涵盖CompletableFuture异步编程、StampedLock读写锁、Flow API响应式流、VarHandle内存访问及结构化并发等核心技术。结合电商订单处理、缓存系统、实时数据流、高性能计数器与用户资料聚合等实战案例,帮助开发者高效构建高并发、低延迟、易维护的Java应用。适合中高级Java开发者提升并发编程能力。
361 0
|
9月前
|
Oracle Java 关系型数据库
java 编程基础入门级超级完整版教程详解
这份文档是针对Java编程入门学习者的超级完整版教程,涵盖了从环境搭建到实际项目应用的全方位内容。首先介绍了Java的基本概念与开发环境配置方法,随后深入讲解了基础语法、控制流程、面向对象编程的核心思想,并配以具体代码示例。接着探讨了常用类库与API的应用,如字符串操作、集合框架及文件处理等。最后通过一个学生成绩管理系统的实例,帮助读者将理论知识应用于实践。此外,还提供了进阶学习建议,引导学员逐步掌握更复杂的Java技术。适合初学者系统性学习Java编程。资源地址:[点击访问](https://pan.quark.cn/s/14fcf913bae6)。
1101 2
|
9月前
|
SQL Java 数据库
解决Java Spring Boot应用中MyBatis-Plus查询问题的策略。
保持技能更新是侦探的重要素质。定期回顾最佳实践和新技术。比如,定期查看MyBatis-Plus的更新和社区的最佳做法,这样才能不断提升查询效率和性能。
500 1
|
9月前
|
Java API 微服务
Java 21 与 Spring Boot 3.2 微服务开发从入门到精通实操指南
《Java 21与Spring Boot 3.2微服务开发实践》摘要: 本文基于Java 21和Spring Boot 3.2最新特性,通过完整代码示例展示了微服务开发全流程。主要内容包括:1) 使用Spring Initializr初始化项目,集成Web、JPA、H2等组件;2) 配置虚拟线程支持高并发;3) 采用记录类优化DTO设计;4) 实现JPA Repository与Stream API数据访问;5) 服务层整合虚拟线程异步处理和结构化并发;6) 构建RESTful API并使用Springdoc生成文档。文中特别演示了虚拟线程配置(@Async)和StructuredTaskSco
1015 0