用户端
HttpClient
通过客户端编程工具包(可以通过java构造和发送http请求),支持HTTP协议
HttpClient 是 Apache Jakarta Common 下的子项目,用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。
HTTP和浏览器有点像,但却不是浏览器。很多人觉得既然HttpClient是一个HTTP客户端编程工具,很多人把他当做浏览器来理解,但是其实HttpClient不是浏览器,它是一个HTTP通信库,因此它只提供一个通用浏览器应用程序所期望的功能子集,最根本的区别是HttpClient中没有用户界面,浏览器需要一个渲染引擎来显示页面,并解释用户输入,例如鼠标点击显示页面上的某处,有一个布局引擎,计算如何显示HTML页面,包括级联样式表和图像。javascript解释器运行嵌入HTML页面或从HTML页面引用的javascript代码。来自用户界面的事件被传递到javascript解释器进行处理。除此之外,还有用于插件的接口,可以处理Applet,嵌入式媒体对象(如pdf文件,Quicktime电影和Flash动画)或ActiveX控件(可以执行任何操作)。HttpClient只能以编程的方式通过其API用于传输和接受HTTP消息。
HttpClient的主要功能:
- 实现了所有 HTTP 的方法(GET、POST、PUT、HEAD、DELETE、HEAD、OPTIONS 等)
- 支持 HTTPS 协议
- 支持代理服务器(Nginx等)等
- 支持自动(跳转)转向
- ……
使用方法
使用HttpClient发送请求、接收响应很简单,一般需要如下几步即可。
-
创建HttpClient对象。
-
创建请求方法的实例,并指定请求URL。如果需要发送GET请求,创建HttpGet对象;如果需要发送POST请求,创建HttpPost对象。
-
如果需要发送请求参数,可调用HttpGet、HttpPost共同的setParams(HttpParams params)方法来添加请求参数;对于HttpPost对象而言,也可调用setEntity(HttpEntity entity)方法来设置请求参数。
-
调用HttpClient对象的execute(HttpUriRequest request)发送请求,该方法返回一个HttpResponse。
-
调用HttpResponse的getAllHeaders()、getHeaders(String name)等方法可获取服务器的响应头;调用HttpResponse的getEntity()方法可获取HttpEntity对象,该对象包装了服务器的响应内容。程序可通过该对象获取服务器的响应内容。
-
释放连接。无论执行方法是否成功,都必须释放连接
/**
* 发送 get请求
*/
public void get() {
//创建httpclient对象
CloseableHttpClient httpclient = HttpClients.createDefault();
// 创建请求对象httpget.
HttpGet httpget = new HttpGet("http://localhost:8080/admin/shop/status");
System.out.println("executing request " + httpget.getURI());
// 发送请求,接受响应结果
CloseableHttpResponse response = httpclient.execute(httpget);
//获取服务端返回的状态码
int statusCode = response.getStatusLine().getStatusCode();
// 获取响应实体
HttpEntity entity = response.getEntity();
String body = EntityUtils.toString(entity)
// 打印响应内容长度
System.out.println("Response content length: " + entity.getContentLength());
// 打印响应内容
System.out.println("Response content: " + body);
System.out.println("------------------------------------");
//关闭资源
response.close();
httpclient.close();
}
/**
* 发送 post请求访问本地应用并根据传递参数不同返回不同结果
*/
public void post() throws Exception {
// 创建默认的httpClient实例对象.
CloseableHttpClient httpclient = HttpClients.createDefault();
// 创建httppost
HttpPost httppost = new HttpPost("http://localhost:8080/admin/employee/login"); //post需要以json提交请求参数
JSONObject jsonObject = new JSONObject();
jsonObject.put("username","admin");
jsonObject.put("password","123456");
//构造StringEntity对象
StringEntity= new StringEntity(jsonObject.toString());//转为JSON格式
//指定请求编码方式
entity.setContentEncoding("uts-8");
//数据格式
entity.setContentType("application/json");
httpPost.setEntity(entity);//把请求封装到post里去
// 发送请求,接受响应结果
CloseableHttpResponse response = httpclient.execute(httpget);
//获取服务端返回的状态码
int statusCode = response.getStatusLine().getStatusCode();
// 获取响应实体
HttpEntity entity1 = response.getEntity();
String body = EntityUtils.toString(entity)
// 打印响应状态
System.out.println(response.getStatusLine());
//关闭资源
response.close();
httpclient.close();
}
微信小程序开发
小程序
目录结构
小程序上传,但只是开发版本,然后在小程序页面提交审核,才能成功发布上线。
微信登录
Path:/user/user/login
第一个user是用户端,第二个user是用户模块
id-是数据库里的唯主键值
openid是在微信里的唯一标识
代码开发
配置项,生成JWT令牌
application.yml
jwt:
#管理端生成JWT令牌
# 设置jwt签名加密时使用的秘钥
admin-secret-key: itcast
# 设置jwt过期时间
admin-ttl: 7200000
# 设置前端传递过来的令牌名称
admin-token-name: token
#给微信
user-token-name: authentication
user-ttl: 7200000
user-secret-key: ${sky.jwt.key}
wechat:
appid: ${sky.wechat.appid}
secret: ${sky.wechat.secret}
mchid: ${sky.dev.wechat.mchid}
mchSerialNo: ${sky.dev.wechat.mchSerialNo}
privateKeyFilePath: ${sky.dev.wechat.privateKeyFilePath}
apiV3Key: ${sky.wechat.apiVersion3Key}
weChatPayCertFilePath: ${sky.dev.wechat.weChatPayCertFilePath}
notifyUrl: ${sky.dev.wechat.notifyUr1}
refundNotifyUrl: ${sky.dev.wechat.refundNotifyUr1}
创建接口对应DTO(接收),VO(响应)
dto.UserLoginDTO.java
vo.UserLoginVO.java
Controller:
@RestController
@Slf4j
@Api(tags = "C端用户相关接口")
@RequestMapping("/user/user")
public class UserController {
@Autowired
private UserService userService;//登录的具体逻辑实现--微信登录--将用户信息(微信授权码)返回给Controller
@Autowired
private JwtProperties jwtProperties;
@PostMapping("/login")
@ApiOperation("用户微信登录")
public Result<UserLoginVO> login(@RequestBody UserLoginDTO userLoginDTO) {
log.info("用户的授权码为:{}", userLoginDTO.getCode());
//微信登录
User user = userService.wxLogin(userLoginDTO);
//用户的主键值
Map<String, Object> claims = new HashMap<>();
claims.put(JwtClaimsConstant.USER_ID, user.getId());
//生成JWT令牌
String jwt = JwtUtil.createJWT(jwtProperties.getUserSecretKey(), jwtProperties.getUserTtl(), claims);
//封装VO对象
UserLoginVO userLoginVO = UserLoginVO.builder()
.openid(user.getOpenid())
.token(jwt)
.id(user.getId())
.build();
return Result.success(userLoginVO);
}
}
Service:
public class UserServiceImpl implements UserService {
//微信服务接口地址--用httpclient调
private static final String WX_LOGIN = "https://api.weixin.qq.com/sns/jscode2session";
@Autowired
private UserMapper userMapper;
@Autowired
private WeChatProperties weChatProperties;
@Override
public User wxLogin(UserLoginDTO userLoginDTO) {
//1.调用微信接口,获得微信用户openid
String openid = getOpenid(userLoginDTO.getCode());
//2.判断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);
}
return user;
}
/**
* 调用微信接口获取openid
*
* @param code
* @return
*/
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");
//获取返回的json字符串
String json = HttpClientUtil.doGet(WX_LOGIN, map);
JSONObject jsonObject = JSON.parseObject(json);
String openid = jsonObject.getString("openid");//取出openid
return openid;
}
}
Mapper:
@Select("select * from user where openid=#{openid}")
User getByOpenid(String openid);
void insert(User user);//需要返回主键值
为了让这个Insert能返回插入之后数据库自动生成的主键值,通过xml修改:
<insert id="insert" parameterType="User" 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>
编写拦截器校验用户端请求头携带的token是否合法
@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.java
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");//在登录之前就查询了
}
商品浏览
涉及这4个接口。
具体代码略。