技术标签: java jwt # SpringBoot 小程序 springboot
登录,微信api
后台获取微信token,包括openid在内
登录流程
1. 小程序发起wx.login请求获取jscode
2. 获取jscode,发送到自己的后端
3. 自己的后台发请求带上jscode, appid(当前用户的唯一标识), secretkey
到微信的后端获取session_key, openid
4. 返回session_key或者openid到页面
5. 页面再发请求到后台验证openid 是否存在, 存在即登录。
不存在 保存openid到个人数据库,即注册。
6. 登录成功后,返回jwt token签名
7. 页面通过微信app.js 的globalData保存openid,jwt token信息
create table forum_info.wei_xin_user
(
id int auto_increment primary key,
user_name varchar(30) null,
user_pwd varchar(30) null,
# 这里的密码没用,登录只是验证open_id,
# 所以说小程序登录是不需要密码的额。它问你是否授权。
# 然后收集你的其它个人信息。
create_date date null,
wx_open_id varchar(30) null
);
package top.bitqian.entity;
import java.util.Date;
import java.io.Serializable;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* <p>
* 微信用户注册实体类~
* </p>
*
* @author echo lovely
* @since 2020-12-04
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class WeiXinUser implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(type = IdType.AUTO)
private Integer id;
private String userName;
private String userPwd;
private Date createDate;
private String wxOpenId;
}
文档里面的字段搬过来。
package top.bitqian.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* <p>
* 调用微信接口 返回授权信息~
* </p>
* @author echo lovely
* @date 2020/12/4 21:56
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Code2Session {
//用户唯一标识
private String openid;
// 会话密钥
private String session_key;
// 用户在开放平台的唯一标识符
private String unionid;
// 错误码
private Integer errcode;
// 错误信息
private String errmsg;
}
package top.bitqian.config;
/**
* @author echo lovely
* @date 2020/12/4 22:03
*/
public class WeiXinPostParamConstant {
/**
* 小程序appid
*/
public static String APP_ID = "小程序开发者官网生成";
/**
* 小程序 appSecret
*/
public static String SECRET = "小程序开发者官网生成";
/**
* 登录时获取的 code
*/
public static String JS_CODE = "";
/**
* 授权类型,此处只需填写 authorization_code
* grant_type ctrl + shit + u
*/
public static String GRANT_TYPE = "";
}
这里是boot,省略了version
<!-- http工具类 -->
<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<!--<version>4.5.6</version>-->
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpcore -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<!--<version>4.4.10</version>-->
</dependency>
package top.bitqian.config;
import java.io.IOException;
import java.io.InputStream;
import java.net.SocketTimeoutException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Map;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.config.RequestConfig.Builder;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
/**
* HTTPClient 工具类.
* @author echo lovely
* @date 2020/12/4 21:44
*/
@Configuration
@SuppressWarnings(value = "all")
public class HttpClientUtil {
private static final Logger logger = LoggerFactory.getLogger(HttpClientUtil.class);
private static PoolingHttpClientConnectionManager connectionManager;
private static String DEFAULT_STR = "";
private static String UTF_8 = "UTF-8";
private final static int CONNECT_TIMEOUT = 3000;// 连接超时毫秒 ps:表示建立连接的超时时间
private final static int SOCKET_TIMEOUT = 10000;// 传输超时毫秒 ps:表示数据传输处理时间
private final static int REQUESTCONNECT_TIMEOUT = 2000;// 从线程池获取连接超时时间毫秒
private final static int MAX_TOTAL = 50;// 线程池的最大连接数
private final static int CONNECT_DEFAULT_ROUTE = 5;// 每个路由默认基础的连接数
private static void init() {
if (connectionManager == null) {
connectionManager = new PoolingHttpClientConnectionManager();
connectionManager.setMaxTotal(MAX_TOTAL);// 整个连接池最大连接数
// 可用空闲连接过期时间,重用空闲连接时会先检查是否空闲时间超过这个时间,如果超过,释放socket重新建立
//connectionManager.setValidateAfterInactivity(50000);
connectionManager.setDefaultMaxPerRoute(CONNECT_DEFAULT_ROUTE);// 每路由最大连接数,默认值是2
}
}
/**
* 通过连接池获取HttpClient
*
* @return
*/
private static CloseableHttpClient getHttpClient() {
init();
Builder builder = RequestConfig.custom();
RequestConfig config = builder.setSocketTimeout(SOCKET_TIMEOUT)
.setConnectTimeout(CONNECT_TIMEOUT)
.setConnectionRequestTimeout(REQUESTCONNECT_TIMEOUT)
.build();
CloseableHttpClient client = HttpClients.custom()
.setMaxConnPerRoute(CONNECT_DEFAULT_ROUTE)
.disableConnectionState().setDefaultRequestConfig(config)
.setConnectionManager(connectionManager).build();
return client;
}
/**
* @param url
* @return
* @throws IOException
*/
public static String httpGetRequest(String url) throws Exception {
HttpGet httpGet = new HttpGet(url);
return getResult(httpGet);
}
public static String httpGetRequest(String url, Map<String, Object> params) throws Exception {
URIBuilder ub = new URIBuilder();
ub.setPath(url);
ArrayList<NameValuePair> pairs = covertParams2NVPS(params);
ub.setParameters(pairs);
HttpGet httpGet = new HttpGet(ub.build());
return getResult(httpGet);
}
public static String httpGetRequest(String url, Map<String, Object> headers
, Map<String, Object> params) throws Exception {
URIBuilder ub = new URIBuilder();
ub.setPath(url);
ArrayList<NameValuePair> pairs = covertParams2NVPS(params);
ub.setParameters(pairs);
HttpGet httpGet = new HttpGet(ub.build());
for (Map.Entry<String, Object> param : headers.entrySet()) {
httpGet.addHeader(param.getKey(), String.valueOf(param.getValue()));
}
return getResult(httpGet);
}
public static String httpPostRequest(String url) throws IOException {
HttpPost httpPost = new HttpPost(url);
return getResult(httpPost);
}
public static String httpPostRequest(String url, Map<String, Object> params) throws Exception {
HttpPost httpPost = new HttpPost(url);
ArrayList<NameValuePair> pairs = covertParams2NVPS(params);
httpPost.setEntity(new UrlEncodedFormEntity(pairs, UTF_8));
return getResult(httpPost);
}
public static String httpPostRequest(String url, String jsonParams) throws Exception {
HttpPost httpPost = new HttpPost(url);
StringEntity se = new StringEntity(jsonParams, UTF_8);
httpPost.setEntity(se);
httpPost.setHeader("Content-Type", "application/json");
return getResult(httpPost);
}
public static String httpPostXMLDataRequest(String url, String xmlData) throws Exception {
HttpPost httpPost = new HttpPost(url);
httpPost.addHeader("Content-Type", "text/xml");
httpPost.setEntity(new StringEntity(xmlData, UTF_8));
return getResult(httpPost);
}
/**
* post
*
* @param url (a=3&b=2 形式)
* @param headers 请求头
* @param params 参数
* @return
* @throws IOException
*/
public static String httpPostRequest(String url, Map<String, Object> headers
, Map<String, Object> params) throws Exception {
HttpPost httpPost = new HttpPost(url);
for (Map.Entry<String, Object> param : headers.entrySet()) {
httpPost.addHeader(param.getKey(), String.valueOf(param.getValue()));
}
ArrayList<NameValuePair> pairs = covertParams2NVPS(params);
httpPost.setEntity(new UrlEncodedFormEntity(pairs, UTF_8));
return getResult(httpPost);
}
private static ArrayList<NameValuePair> covertParams2NVPS(Map<String, Object> params) {
ArrayList<NameValuePair> pairs = new ArrayList<NameValuePair>();
for (Map.Entry<String, Object> param : params.entrySet()) {
pairs.add(new BasicNameValuePair(param.getKey(), String.valueOf(param.getValue())));
}
return pairs;
}
/**
* post
*
* @param url (JSON 形式)
* @param headers 请求头
* @param params 参数
* @return
* @throws IOException
*/
public static String httpPostRequest(String url, Map<String, Object> headers, String jsonParams
) throws Exception {
HttpPost httpPost = new HttpPost(url);
for (Map.Entry<String, Object> param : headers.entrySet()) {
httpPost.setHeader(param.getKey(), String.valueOf(param.getValue()));
}
StringEntity se = new StringEntity(jsonParams, UTF_8);
httpPost.setEntity(se);
return getResult(httpPost);
}
/**
* 处理Http请求
*
* @param request
* @return string
* @throws IOException
*/
private static String getResult(HttpRequestBase request) throws IOException {
CloseableHttpClient httpClient = getHttpClient();
CloseableHttpResponse response = null;
InputStream in = null;
try {
response = httpClient.execute(request);
HttpEntity entity = response.getEntity();
in = response.getEntity().getContent();
if (entity != null) {
String result = EntityUtils.toString(entity, Charset.forName(UTF_8));
response.close();
return result;
}
} catch (ConnectTimeoutException e) {
// 连接超时异常
logger.error("connect timeout {}", e);
} catch (SocketTimeoutException e) {
// 读取超时异常
logger.error("read timeout {}", e);
} catch (ClientProtocolException e) {
// 该异常通常是协议错误导致:比如构造HttpGet对象时传入协议不对(将'http'写成'htp')or响应内容不符合
logger.error("protocol exception {}", e);
} catch (ParseException e) {
// 解析异常
logger.error("parse exception {}", e);
} catch (IOException e) {
// 该异常通常是网络原因引起的,如HTTP服务器未启动等
logger.error("network exception {}", e);
} catch (Exception e) {
logger.error("other exception {}", e);
} finally {
if (response != null) {
try {
response.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//in.close();作用就是将用完的连接释放,下次请求可以复用
//这里特别注意的是,如果不使用in.close();而仅仅使用response.close();结果就是连接会被关闭,并且不能被复用,这样就失去了采用连接池的意义。
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return DEFAULT_STR;
}
public static void main(String[] args) {
String str = null;
try {
str = HttpClientUtil.httpGetRequest("https://www.baidu.com");
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(str);
}
}
json转实体类用
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
/**
* 获取openid session_key
* @param jsCode 小程序请求到的jsCode
* @return 授权信息~
*/
@Override
public Code2Session getWinXinJson(String jsCode) {
// https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
StringBuilder url = new StringBuilder();
url.append("https://api.weixin.qq.com/sns/jscode2session?appid=");
url.append(WeiXinPostParamConstant.APP_ID);
url.append("&secret=");
url.append(WeiXinPostParamConstant.SECRET);
url.append("&js_code=");
url.append(jsCode);
url.append("&grant_type=authorization_code");
try {
String weiXinJson = HttpClientUtil.httpGetRequest(url.toString());
System.out.println(weiXinJson);
return new ObjectMapper().readValue(weiXinJson, Code2Session.class);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
package top.bitqian.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import top.bitqian.config.AuthTokenCheck;
import top.bitqian.entity.Code2Session;
import top.bitqian.entity.WeiXinUser;
import top.bitqian.service.WeiXinUserService;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
/**
* 微信登录 controller~
* @author echo lovely
* @date 2020/12/4 21:36
*/
@RestController
@Slf4j
public class WeiXinUserController {
@Autowired
private WeiXinUserService weiXinUserService;
// 调用微信的接口获取 app_id
@RequestMapping("/getCode/{jsCode}")
public Code2Session getWinXinJson(@PathVariable("jsCode") String jsCode) {
return weiXinUserService.getWinXinJson(jsCode);
}
// 用户提交wx_id 过来注册
@RequestMapping("/wx_user/register")
public boolean doRegister(WeiXinUser user) {
// 账号存在..
WeiXinUser tmpUser = weiXinUserService.getWeiXinUserByOpenId(user.getWxOpenId());
if (tmpUser != null) {
return false;
}
// 不存在,即注册
user.setCreateDate(new Date(System.currentTimeMillis()));
weiXinUserService.addWeiXinUser(user);
return true;
}
@RequestMapping("/wx_user/login")
public String doLogin(WeiXinUser weiXinUser) throws Exception {
return weiXinUserService.doLogin(weiXinUser);
}
@AuthTokenCheck
@RequestMapping("/wx_user/msg")
public String testJwtToken(HttpServletRequest request) {
Object userId = request.getAttribute("id");
System.out.println("userId from page token~=======>" + userId);
return "auth by token~" + userId;
}
}
package top.bitqian.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import top.bitqian.config.HttpClientUtil;
import top.bitqian.config.JwtUtil;
import top.bitqian.config.WeiXinPostParamConstant;
import top.bitqian.entity.Code2Session;
import top.bitqian.entity.WeiXinUser;
import top.bitqian.mapper.WeiXinUserMapper;
import top.bitqian.service.WeiXinUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 服务实现类
* </p>
*
* @author echo lovely
* @since 2020-12-04
*/
@Service
public class WeiXinUserServiceImpl extends ServiceImpl<WeiXinUserMapper, WeiXinUser> implements WeiXinUserService {
/**
* 获取openid session_key
* @param jsCode 小程序请求到的jsCode
* @return 授权信息~
*/
@Override
public Code2Session getWinXinJson(String jsCode) {
// https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
StringBuilder url = new StringBuilder();
url.append("https://api.weixin.qq.com/sns/jscode2session?appid=");
url.append(WeiXinPostParamConstant.APP_ID);
url.append("&secret=");
url.append(WeiXinPostParamConstant.SECRET);
url.append("&js_code=");
url.append(jsCode);
url.append("&grant_type=authorization_code");
try {
String weiXinJson = HttpClientUtil.httpGetRequest(url.toString());
System.out.println(weiXinJson);
return new ObjectMapper().readValue(weiXinJson, Code2Session.class);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Autowired
private WeiXinUserMapper userMapper;
public WeiXinUser getWeiXinUserByOpenId(String openId) {
WeiXinUser tmpUser = new WeiXinUser();
tmpUser.setWxOpenId(openId);
QueryWrapper<WeiXinUser> queryWrapper = new QueryWrapper<>(tmpUser);
return userMapper.selectOne(queryWrapper);
}
@Override
public String doLogin(WeiXinUser user) throws Exception {
// 登录需要:
// 1. 根据小程序传来的openid验证数据库中的id,看是否存在~
WeiXinUser weiXInUser = getWeiXinUserByOpenId(user.getWxOpenId());
System.out.println("doLogin----->" + weiXInUser);
if (weiXInUser != null) {
// 2. 存在 返回jwt签名~ 页面保存
return JwtUtil.createToken(weiXInUser);
}
// 2. 不存在 return ""
return null;
}
@Override
public void addWeiXinUser(WeiXinUser user) {
// userMapper.addWeiXinUser(user);
userMapper.insert(user);
}
}
package top.bitqian.config;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 要给哪些方法进行token验证
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AuthTokenCheck {
}
package top.bitqian.config;
import com.auth0.jwt.interfaces.Claim;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Map;
/**
* 拦截器
* @author echo lovely
* @date 2020/12/5 20:28
*/
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
/**
* 拦截器需要被注册进..
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TokenInterceptor());
}
}
class TokenInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
System.out.println("进来l...");
// 对方法进行拦截
if (! (handler instanceof HandlerMethod)) {
return true;
}
// 方法对象
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method methodTarget = handlerMethod.getMethod();
// 对加了AuthTokenCheck注解 的方法进行token验证~
boolean isTokenTarget = methodTarget.isAnnotationPresent(AuthTokenCheck.class);
if (isTokenTarget) {
// 进行token验证, 头部里面的token
// String authorizeToken = request.getHeader("authorize_token");
String authorizeToken = request.getParameter("authorize_token");
try {
Map<String, Claim> claimMap = JwtUtil.parseToken(authorizeToken);
// 解析获取token中的用户id~ 也可根据相应的键获取其它信息
Integer id = claimMap.get("id").asInt();
String userName = claimMap.get("userName").asString();
System.out.println(id + "\t" + userName);
// 放入request中
request.setAttribute("id", id);
return true;
} catch (Exception e) {
return false;
}
}
return true;
}
}
前提:已成功安装superset(若未安装,请参照:1、打开命令行窗口使用“windows+r"打开运行窗口,输入cmd,回车,打开命令行窗口2、切换到superset的安装目录的bin路径下cd XXX\Lib\site-packages\superset\bin3、启动supersetactivate supersetpython superset run -debugger3、登录superset4、进行mysql数据库的连接最后滑到
突然有一天部署在服务器的一个应用挂掉了,没办法只能进入服务器打开【事件查看器】查看下,好不容易找到了打开后一脸懵逼事件查看器查到的内容根本对我们排障没有任何作用。在这个时候如果有对应的dump文件就能派上用场了,只要有dump文件就能查到应用挂掉那刻的一手情报,可能有人认为分析dump文件是非常难的事情,但是最近不断有新的d..._怎么用vs看dump的堆栈
本文主要描述了如果将Java项目和Vue项目从本地打包到云服务器上,包括前期的环境配置以及如果让项目保持在后台挂载,由于不熟悉,在部署项目的时候花了很长时间,可能出现的问题也会在本文中列举出来,没有特殊情况的话,按照这个流程会很顺利的实现项目部署和运行。_java项目部署到云服务器
问题描述每次开第三台虚拟机时,电脑直接蓝屏。问题原因三台虚拟机的处理器内核过多,三台虚拟机配置的“处理器内核总数”都是4。解决问题方法调小每台虚拟机的“处理器数量”或者“每个处理器的内核数量”即可..._虚拟机核心数多了会怎么样
20210925yolo实现交通信号灯视频流识别调试过程所用代码:基于YOLOv3的红绿灯检测识别(Python源码可直接运行)原作者是tensorflow1,我的环境是tensorflow2,遇到一堆版本导致的问题。现在就是后悔,很后悔,十分后悔。遇到报错就度娘,好在最终还是在tensorflow2下运行成功了。报错1、SystemError: unknown opcode我的环境是python3.6,代码作者应该是python3.5(作者未说明)解决:python3.5训练的模型不能直接_systemerror: unknown opcode
这种错误通常是内存管理的问题,一般是访问了已经释放的对象导致的,可以开启僵尸对象(ZombieObjects)来定位问题:在Xcode的菜单:Product->Scheme->EditScheme->Runxxx.app右边的选项中,选中EnableZombieObjects开启该选项后,程序在运行时,如果访问了已经释放的对象,则会给出较准确的定位信...
命令太多记不住可以参考Redis中文网,强烈建议收藏一、字符串 stringsPython操作Redis的redis模块对字符串(string)的主要操作函数包括:SET、GET、GETSET、SETEX、SETNX、MSET、MSETNX、INCR(INCRBY,DECR,DECRBY在python中庸同一个函数incr实现)、APPEND、SETRANGE、STRLEN。函数说明如下:SET:..._python redis getlist
The problems during the debugging process 1) Q:How to use the IP core and what the appropriate way to solve it ? A:Using the the help document of ISE 14.7, also using the datasheet from the offi_blk_mem_gen_v7_3
Transformers for data prepocessing:DictVectorizer -- one-hot encoder StandardScaler -- scaled data has zero mean and unit variance. Noticed: if the object could be saved, then the same operation...
Ignatius has just come back school from the 30th ACM/ICPC. Now he has a lot of homework to do. Every teacher gives him a deadline of handing in the homework. If Ignatius hands in the homework after the_hdu - 1789
1022: [SHOI2008]小约翰的游戏JohnTime Limit:1 SecMemory Limit:162 MBSubmit:2976Solved:1894[Submit][Status][Discuss]Description 小约翰经常和他的哥哥玩一个非常有趣的游戏:桌子上有n堆石子,小约翰和他的哥哥轮流取石子,每个人取的时候...
1. 共享内存的方法Unix: QSharedMemory "owns" the shared memory segment. When the last thread or process that has an instance of QSharedMemory attached to a particular shared memory segment detaches from the_程序已运行,不能运行多个实例