微信登录、商品浏览
HttpClient
介绍
- HttpClient 是Apache Jakarta Common 下的子项目,可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。
- Maven坐标:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.13</version>
</dependency>
- 核心API:HttpClient、HttpClients、CloseableHttpClient、HttpGet、HttpPost。
- 发送请求步骤:
- 创建HttpClient对象。
- 创建Http请求对象。
- 调用HttpClient的execute方法发送请求。
入门案例
//http客户端对象,可以发送http请求
CloseableHttpClient httpClient = HttpClients.createDefault();
//构造Get方式请求
HttpGet httpGet = new HttpGet("http://localhost:8080/user/shop/status");
//发送请求
CloseableHttpResponse response = httpClient.execute(httpGet);
//http响应码
int statusCode = response.getStatusLine().getStatusCode();
//http响应体
HttpEntity entity = response.getEntity();
//将响应体转为String字符串
String body = EntityUtils.toString(entity);
System.out.println(body);
//关闭资源
response.close();
httpClient.close();
CloseableHttpClient httpClient = HttpClients.createDefault();
//Post方式请求
HttpPost httpPost = new HttpPost("http://localhost:8080/admin/employee/login");
//构造json数据
JSONObject jsonObject = new JSONObject();
jsonObject.put("username","admin");
jsonObject.put("password", "123456");
//构造请求体
StringEntity stringEntity = new StringEntity(jsonObject.toString());
//设置请求编码
stringEntity.setContentEncoding("utf-8");
//设置数据类型
stringEntity.setContentType("application/json");
//设置当前Post请求的请求体
httpPost.setEntity(stringEntity);
//发送请求
CloseableHttpResponse response = httpClient.execute(httpPost);
//http响应码
int statusCode = response.getStatusLine().getStatusCode();
//http响应体
HttpEntity entity = response.getEntity();
//将响应体转为String字符串
String body = EntityUtils.toString(entity);
System.out.println(body);
//关闭资源
response.close();
httpClient.close();
微信小程序开发
介绍
- 微信小程序是一种新的开放能力,可以在微信内被便捷地获取和传播,同时具有出色的使用体验。
- 官网:https://mp.weixin.qq.com/cgi-bin/wx?token=&lang=zh_CN
- 开放注册范围分为:个人(个人注册不能使用微信支付功能,也无法获取到用户的手机号)、企业、政府、媒体和其他组织。
- 开发支持:微信小程序提供了一系列工具帮助开发者快速接入并完成小程序开发:开发文档、开发者工具、设计指南和小程序体验DEMO。
- 接入流程:
- 注册:在微信公众平台注册小程序,完成注册后可以同步进行信息完善和开发。
- 小程序信息完善:填写小程序基本信息,包括名称、头像、介绍及服务范围等。
- 开发小程序:完成小程序开发者绑定、开发信息配置后,开发者可下载开发者工具、参考开发文档进行小程序的开发和调试。
- 提交审核和发布:完成小程序开发后,提交代码至微信团队审核,审核通过后即可发布(公测期间不能发布)。
准备工作
注册小程序
完善小程序信息
- 登录小程序后台:https://mp.weixin.qq.com/
- 完善小程序信息、小程序类目。
- 查看小程序的 AppID。
下载开发者工具
- 下载地址: https://developers.weixin.qq.com/miniprogram/dev/devtools/stable.html
- 扫描登录开发者工具。
- 创建小程序项目。
- 熟悉开发者工具布局。
- 设置不校验合法域名。
入门案例
- 操作步骤:
- 了解小程序目录结构。
- 编写小程序代码。
- 编译小程序。
了解小程序目录结构
- 小程序包含一个描述整体程序的 app 和多个描述各自页面的 page。
- 一个小程序主体部分由三个文件组成,必须放在项目的根目录,如下:
| 文件 |
必需 |
作用 |
| app.js |
是 |
小程序逻辑 |
| app.json |
是 |
小程序公共配置 |
| app.wxss |
否 |
小程序公共样式表 |
| 文件类型 |
必需 |
作用 |
| js |
是 |
页面逻辑 |
| wxml |
是 |
页面结构 |
| json |
否 |
页面配置 |
| wxss |
否 |
页面样式表 |
编写小程序代码
<view class="container">
<view>{{msg}}</view>
<view>
<button type="default" bindtap="getUserInfo">获取用户信息</button>
<image style="width: 100px;height: 100px;" src="{{avatarUrl}}"></image>
{{nickName}}
</view>
<view>
<button type="primary" bindtap="wxlogin">微信登录</button>
授权码:{{code}}
</view>
<view>
<button type="warn" bindtap="sendRequest">发送请求</button>
响应结果:{{result}}
</view>
</view>
Page({
data:{
msg:'hello world',
avatarUrl:'',
nickName:'',
code:'',
result:''
},
getUserInfo:function(){
wx.getUserProfile({
desc: '获取用户信息',
success:(res) => {
console.log(res)
this.setData({
avatarUrl:res.userInfo.avatarUrl,
nickName:res.userInfo.nickName
})
}
})
},
wxlogin:function(){
wx.login({
success: (res) => {
console.log("授权码:"+res.code)
this.setData({
code:res.code
})
}
})
},
sendRequest:function(){
wx.request({
url: 'http://localhost:8080/user/shop/status',
method:'GET',
success:(res) => {
console.log("响应结果:" + res.data.data)
this.setData({
result:res.data.data
})
}
})
}
})
编译小程序
发布小程序
- 在微信开发者工具中点击上传。
- 在微信公众平台的版本管理中的开发版本中点击提交审核。
- 等待审核通过。
- 审核通过后,在审核版本中点击发布。
微信登录
导入小程序代码
微信登录流程
需求分析和设计
产品原型
业务规则
- 基于微信登录实现小程序的登录功能。
- 如果是新用户需要自动完成注册。
接口设计
数据库设计
| 字段名 |
数据类型 |
说明 |
备注 |
| id |
bigint |
主键 |
自增 |
| openid |
varchar(45) |
微信用户的唯一标识 |
|
| name |
varchar(32) |
用户姓名 |
|
| phone |
varchar(11) |
手机号 |
|
| sex |
varchar(2) |
性别 |
|
| id_number |
varchar(18) |
身份证号 |
|
| avatar |
varchar(500) |
微信用户头像路径 |
|
| create_time |
datetime |
注册时间 |
|
代码开发
配置微信登录所需配置项
#application-dev.yml
sky:
wechat:
appid: appid
secret: appsecret
#application.yml
sky:
wechat:
appid: ${sky.wechat.appid}
secret: ${sky.wechat.secret}
配置为微信用户生成jwt令牌时使用的配置项
#application.yml
sky:
jwt:
# 设置jwt签名加密时使用的秘钥
user-secret-key: zgg1h
# 设置jwt过期时间
user-ttl: 7200000
# 设置前端传递过来的令牌名称
user-token-name: authentication
DTO设计
@Data
public class UserLoginDTO implements Serializable {
private String code;
}
VO设计
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserLoginVO implements Serializable {
private Long id;
private String openid;
private String token;
}
根据接口定义创建UserController的login方法
@RestController
@RequestMapping("/user/user")
@Api(tags = "用户相关接口")
@Slf4j
public class UserController {
@PostMapping("/login")
@ApiOperation("微信登录")
public Result<UserLoginVO> login(@RequestBody UserLoginDTO userLoginDTO) {
return null;
}
}
完善UserController的login方法
@PostMapping("/login")
@ApiOperation("微信登录")
public Result<UserLoginVO> login(@RequestBody UserLoginDTO userLoginDTO) {
log.info("微信用户登录:{}", userLoginDTO.getCode());
UserLoginVO userLoginVO = userService.wxLogin(userLoginDTO);
return Result.success(userLoginVO);
}
创建UserService接口
public interface UserService {
UserLoginVO wxLogin(UserLoginDTO userLoginDTO);
}
创建UserServiceImpl实现类
@Service
@Slf4j
public class UserServiceImpl implements UserService {
@Override
public UserLoginVO wxLogin(UserLoginDTO userLoginDTO) {
return null;
}
}
在UserServiceImpl中创建私有方法getOpenid
@Autowired
private WeChatProperties weChatProperties;
//微信服务接口地址
public static final String WX_LOGIN = "https://api.weixin.qq.com/sns/jscode2session";
private String getOpenid(String code) {
//调用微信接口服务,获得当前用户的openid
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);
String openid = jsonObject.getString("openid");
return openid;
}
完善UserServiceImpl的wxLogin方法
@Autowired
private JwtProperties jwtProperties;
@Autowired
private UserMapper userMapper;
@Override
public UserLoginVO wxLogin(UserLoginDTO userLoginDTO) {
String openid = getOpenid(userLoginDTO.getCode());
//判断openid是否为空,如果为空表示登录失败,抛出业务异常
if (openid == null) {
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);
}
//为微信用户生成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 = UserLoginVO.builder()
.id(user.getId())
.openid(user.getOpenid())
.token(token)
.build();
//返回这个userLoginVO对象
return userLoginVO;
}
创建UserMapper接口
@Mapper
public interface UserMapper {
/**
* 根据openid查询用户
*
* @param openid
* @return
*/
@Select("select * from user where openid = #{openid}")
User getByOpenId(String openid);
void insert(User user);
}
创建UserMapper.xml映射文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sky.mapper.UserMapper">
<insert id="insert" useGeneratedKeys="true" keyProperty="id">
insert into user (openid, name, phone, sex, id_number, avatar, create_time)
values (#{openid}, #{name}, #{phone}, #{sex}, #{idNumber}, #{avatar}, #{createTime})
</insert>
</mapper>
编写拦截器JwtTokenUserInterceptor,统一拦截用户端发送的请求并进行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(jwtTokenAdminInterceptor)
.addPathPatterns("/admin/**")
.excludePathPatterns("/admin/employee/login");
registry.addInterceptor(jwtTokenUserInterceptor)
.addPathPatterns("/user/**")
.excludePathPatterns("/user/user/login")
.excludePathPatterns("/user/shop/status");
}
功能测试
- 可以通过接口文档进行测试,最后完成前后端联调测试即可。
导入商品浏览功能代码
需求分析和设计
产品原型
接口设计
代码导入
功能测试
- 可以通过接口文档进行测试,最后完成前后端联调测试即可。