前言
由于微信官方是无法与我们自己的登录表集成
此篇文章带你写微信公众号根据自己的表进行设计登录系统
项目技术: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.因为我们在拦截器中是判断了请求头中没有拦截器是无法请求的
自定义的登录
开发微信请求方法(项目业务代码)
@SysUserLogin SysUserTokenEntity sysUserTokenEntity 实体类就已经有我们在拦截器查询数据库赋值后的信息了哦
//类路径@RequestMapping("/service/wx/workOrderService") @RequestMapping("captcha.jpg") public voidcaptcha(@SysUserLogin SysUserTokenEntity sysUserTokenEntity) throws IOException { System.out.println(sysUserTokenEntity); // 以下逻辑自定义 }
以上就完成啦!光看没有用,重要的还是实践啊
