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

前言

  1. 由于微信官方是无法与我们自己的登录表集成
  2. 此篇文章带你写微信公众号根据自己的表进行设计登录系统
  3. 项目技术:springboot + mybatisplus
  4. 注意:本篇只是基于微信公众号自定义登录逻辑,如果微信公众号还没有集成的话请先集成微信公众号,本项目采用的是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. 因为我们在拦截器中是判断了请求头中没有拦截器是无法请求的

在这里插入图片描述

自定义的登录

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

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

//类路径@RequestMapping("/service/wx/workOrderService")

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一个小浪吴啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值