课程内容
- HttpClient
- 微信小程序开发
- 微信登录
- 导入商品浏览功能代码
- 实现微信登录功能
- 实现商品浏览功能
HttpClient
HttpClient介绍
- 提供高效 最新 功能丰富的支持http协议的客户端编程工具包
- 支持HTTP协议最新的版本和建议
- 核心api
- HttpClient
- HttpClients
- CloseableHttpClient
- HttpGet
- HttpPost
- 发送请求步骤
- 创建HttpClient对象
- 创建Http请求对象
- 调用HttpClient的execute方法发送请求
- 解析结果
- 关闭资源
- maven坐标
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
- 在aliyun-sdk-oss坐标中底层已经包含了HttpClient相关依赖
- 故无需再次添加
微信小程序开发
微信小程序介绍
- 一种新的开发能力
- 可以在微信内部便捷地获取和传播
- 同时具有出色使用体验
- 接入流程
- 在微信公众平台注册小程序 完成注册后可以同步进行信息完善和开发
- 小程序信息完善
- 开发小程序
- 提交审核和发布
准备工作
- 注册小程序
- 下载开发者工具
- 熟悉开发者工具布局
- 设置不校验合法域名
小程序目录结构
- 包含一个描述整体程序的app
- 多个描述各自页面的page
- 一个小程序文件主体部分由三个文件组成
- app.js 必须存在 主要存档小程序的逻辑代码
- app.json 必须存在 小程序配置文件主要存在公共配置
- app.wxss 非必须 存放公共样式表(类似css)
- 小程序的不同页面存放在pages目录
- js文件 必须存在 页面逻辑代码
- wxml文件 必须存在 存放页面结构 类似html
- json文件 非必须 存放页面相关配置
- wxss文件 非必须 存放公共样式表(类似css)
微信登录
导入小程序代码
- 黑马官方资料已经提供小程序代码
- 导入即可
- 使用自己的appid
- 在common–>vendor.js文件中
- 搜索baseUri 将后端服务改为自己的ip地址和端口号
微信登录流程
步骤分析
- 小程序端,调用wx.login()获取code,就是授权码。
- 小程序端,调用wx.request()发送请求并携带code,请求开发者服务器(自己编写的后端服务)。
- 开发者服务端,通过HttpClient向微信接口服务发送请求,并携带appId+appsecret+code三个参数。
- 开发者服务端,接收微信接口服务返回的数据,session_key+opendId等。opendId是微信用户的唯一标识。
- 开发者服务端,自定义登录态,生成令牌(token)和openid等数据返回给小程序端,方便后绪请求身份校验。
- 小程序端,收到自定义登录态,存储storage。
- 小程序端,后绪通过wx.request()发起业务请求时,携带token。
- 开发者服务端,收到请求后,通过携带的token,解析当前登录用户的id。
- 开发者服务端,身份校验通过后,继续相关的业务逻辑处理,最终返回业务数据。
需求分析与设计
- 基于微信登录实现小程序的登录功能
- 如果是新用户需要自动完成注册
表设计
字段名 |
数据类型 |
说明 |
备注 |
id |
bigint |
主键 |
自增 |
openid |
varchar(45) |
微信用户的唯一标识 |
|
name |
varchar(32) |
用户姓名 |
|
phone |
varchar(11) |
手机号 |
|
sex |
varchar(2) |
性别 |
|
id_number |
varchar(18) |
身份证号 |
|
avatar |
varchar(500) |
微信用户头像路径 |
|
create_time |
datetime |
注册时间 |
|
代码开发
配置所需配置项
sky:
wechat:
appid: ${sky.wechat.appid}
secret: ${sky.wechat.secret}
Controller层
- 根据接口定义创建UserController类
- 在controller中新建user.UserController
- 创建Login方法
@Slf4j
@RequestMapping("/user/user")
@RestController
@Api(tags = "用户端用户相关接口")
public class UserController {
@Autowired
private UserService userService;
@Autowired
private JwtProperties jwtProperties;
@ApiOperation("微信登录")
@PostMapping("/login")
public Result<UserLoginVO> login(@RequestBody UserLoginDTO userLoginDTO){
User user = userService.wxlogin(userLoginDTO);
//微信登录
//为微信用户生成jwt令牌
Map<String,Object> claims = new HashMap<>();
claims.put(JwtClaimsConstant.USER_ID,user.getId());
String token = JwtUtil.createJWT(
jwtProperties.getUserSecretKey(),
jwtProperties.getUserTtl(),
claims);
UserLoginVO userLoginVO = UserLoginVO.builder()
.id(user.getId())
.openid(user.getOpenid())
.token(token)
.build();
return Result.success(userLoginVO);
}
}
Service层
- 先创建UserService层接口
- 无需单独创建user文件包
- 在接口中声明wxLogin方法
public interface UserService {
//微信登录
User wxlogin(UserLoginDTO userLoginDTO);
}
@Service
@Slf4j
public class UserServiceImpl implements UserService {
//微信服务接口地址
public static final String WX_LOGIN = "https://api.weixin.qq.com/sns/jscode2session";
@Autowired
private WeChatProperties weChatProperties;
@Autowired
private UserMapper userMapper;
@Override
public User wxlogin(UserLoginDTO userLoginDTO) {
String openid = getOpenId(userLoginDTO.getCode());
//判断openid是否为空 如果为空表示登录失败
if ("0".equals(openid)){
throw new LoginFailedException(MessageConstant.LOGIN_FAILED);
}
//判断当前用户是否为新用户
User user = userMapper.getByOpenid(openid);
//如果是新用户 则注册用户信息
if (user==null){
user = User.builder()
.openid(openid)
.createTime(LocalDateTime.now())
.build();
userMapper.insert(user);
}
//返回用户对象
return user;
}
//调用微信接口方法
private String getOpenId(String code){
Map<String,String> map = new HashMap<>();
map.put("appid",weChatProperties.getAppid());
map.put("secret",weChatProperties.getSecret());
map.put("js_code",code);
map.put("grant_type","authorization_code");
String json = HttpClientUtil.doGet(WX_LOGIN, map);
JSONObject jsonObject= JSON.parseObject(json);
return jsonObject.getString("openid");
}
}
编写拦截器
- 新建JwtTokenUserInterceptor
package com.sky.interceptor;
import com.sky.constant.JwtClaimsConstant;
import com.sky.context.BaseContext;
import com.sky.properties.JwtProperties;
import com.sky.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* jwt令牌校验的拦截器
*/
@Component
@Slf4j
public class JwtTokenUserInterceptor implements HandlerInterceptor {
@Autowired
private JwtProperties jwtProperties;
/**
* 校验jwt
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//判断当前拦截到的是Controller的方法还是其他资源
if (!(handler instanceof HandlerMethod)) {
//当前拦截到的不是动态方法,直接放行
return true;
}
//1、从请求头中获取令牌
String token = request.getHeader(jwtProperties.getUserTokenName());
//2、校验令牌
try {
log.info("jwt校验:{}", token);
Claims claims = JwtUtil.parseJWT(jwtProperties.getUserSecretKey(), token);
Long userId = Long.valueOf(claims.get(JwtClaimsConstant.USER_ID).toString());
log.info("当前用户的id:", userId);
BaseContext.setCurrentId(userId);
//3、通过,放行
return true;
} catch (Exception ex) {
//4、不通过,响应401状态码
response.setStatus(401);
return false;
}
}
}
- 在WebMvcConfiguration添加以下内容
@Autowired
private JwtTokenUserInterceptor jwtTokenUserInterceptor;
/**
* 注册自定义拦截器
* @param registry
*/
protected void addInterceptors(InterceptorRegistry registry) {
log.info("开始注册自定义拦截器...");
//.........
registry.addInterceptor(jwtTokenUserInterceptor)
.addPathPatterns("/user/**")
.excludePathPatterns("/user/user/login")
.excludePathPatterns("/user/shop/status");
}
导入商品浏览功能代码