基于redis的小程序登錄實(shí)現(xiàn)方法流程分析
這張圖是小程序的登錄流程解析:

小程序登陸授權(quán)流程:
在小程序端調(diào)用wx.login()獲取code,由于我是做后端開(kāi)發(fā)的這邊不做贅述,直接貼上代碼了.有興趣的直接去官方文檔看下,鏈接放這里: wx.login()
wx.login({
success (res) {
if (res.code) {
//發(fā)起網(wǎng)絡(luò)請(qǐng)求
wx.request({
url: 'https://test.com/onLogin',
data: {
code: res.code
}
})
} else {
console.log('登錄失敗!' + res.errMsg)
}
}
})
小程序前端登錄后會(huì)獲取code,調(diào)用自己的開(kāi)發(fā)者服務(wù)接口,調(diào)用個(gè)get請(qǐng)求:
需要得四個(gè)參數(shù):
appid:小程序appid
secret: 小程序密鑰
js_code: 剛才獲取的code
grant_type:‘a(chǎn)uthorization_code' //這個(gè)是固定的
如果不出意外的話,微信接口服務(wù)器會(huì)返回四個(gè)參數(shù):

詳情可以看下官方文檔:jscode2session
下面附上我的代碼:
@AuthIgnore
@RequestMapping("/login")
@ResponseBody
public ResponseBean openId(@RequestParam(value = "code", required = true) String code,
@RequestParam(value = "avatarUrl") String avatarUrl,
@RequestParam(value = "city") String city,
@RequestParam(value = "country") String country,
@RequestParam(value = "gender") String gender,
@RequestParam(value = "language") String language,
@RequestParam(value = "nickName") String nickName,
@RequestParam(value = "province") String province,
HttpServletRequest request) { // 小程序端獲取的CODE
ResponseBean responseBean = new ResponseBean();
try {
boolean check = (StringUtils.isEmpty(code)) ? true : false;
if (check) {
responseBean = new ResponseBean(false, UnicomResponseEnums.NO_CODE);
return responseBean;
}
//將獲取的用戶數(shù)據(jù)存入數(shù)據(jù)庫(kù);
Map<String, Object> msgs = new HashMap<>();
msgs.put("appid", appId);
msgs.put("secret", secret);
msgs.put("js_code", code);
msgs.put("grant_type", "authorization_code");
// java的網(wǎng)絡(luò)請(qǐng)求,返回字符串
String data = HttpUtils.get(msgs, Constants.JSCODE2SESSION);
logger.info("======> " + data);
String openId = JSONObject.parseObject(data).getString("openid");
String session_key = JSONObject.parseObject(data).getString("session_key");
String unionid = JSONObject.parseObject(data).getString("unionid");
String errcode = JSONObject.parseObject(data).getString("errcode");
String errmsg = JSONObject.parseObject(data).getString("errmsg");
JSONObject json = new JSONObject();
int userId = -1;
if (!StringUtils.isBlank(openId)) {
Users user = userService.selectUserByOpenId(openId);
if (user == null) {
//新建一個(gè)用戶信息
Users newUser = new Users();
newUser.setOpenid(openId);
newUser.setArea(city);
newUser.setSex(Integer.parseInt(gender));
newUser.setNickName(nickName);
newUser.setCreateTime(new Date());
newUser.setStatus(0);//初始
if (!StringUtils.isBlank(unionid)) {
newUser.setUnionid(unionid);
}
userService.instert(newUser);
userId = newUser.getId();
} else {
userId = user.getId();
}
json.put("userId", userId);
}
if (!StringUtils.isBlank(session_key) && !StringUtils.isBlank(openId)) {
//這段可不用redis存,直接返回session_key也可以
String userAgent = request.getHeader("user-agent");
String sessionid = tokenService.generateToken(userAgent, session_key);
//將session_key存入redis
redisUtil.setex(sessionid, session_key + "###" + userId, Constants.SESSION_KEY_EX);
json.put("token", sessionid);
responseBean = new ResponseBean(true, json);
} else {
responseBean = new ResponseBean<>(false, null, errmsg);
}
return responseBean;
} catch (Exception e) {
e.printStackTrace();
responseBean = new ResponseBean(false, UnicomResponseEnums.JSCODE2SESSION_ERRO);
return responseBean;
}
}
解析:
這邊我的登錄獲取的session_key出于安全性考慮沒(méi)有直接在前端傳輸,而是存到了redis里面給到前端session_key的token傳輸,
而且session_key的銷毀時(shí)間是20分鐘,時(shí)間內(nèi)可以重復(fù)獲取用戶數(shù)據(jù).
如果只是簡(jiǎn)單使用或者對(duì)安全性要求不嚴(yán)的話可以直接傳session_key到前端保存.
session_key的作用:
校驗(yàn)用戶信息(wx.getUserInfo(OBJECT)返回的signature);
解密(wx.getUserInfo(OBJECT)返回的encryptedData);
按照官方的說(shuō)法,wx.checksession是用來(lái)檢查 wx.login(OBJECT) 的時(shí)效性,判斷登錄是否過(guò)期;
疑惑的是(openid,unionid )都是用戶唯一標(biāo)識(shí),不會(huì)因?yàn)閣x.login(OBJECT)的過(guò)期而改變,所以要是沒(méi)有使用wx.getUserInfo(OBJECT)獲得的用戶信息,確實(shí)沒(méi)必要使用wx.checksession()來(lái)檢查wx.login(OBJECT) 是否過(guò)期;
如果使用了wx.getUserInfo(OBJECT)獲得的用戶信息,還是有必要使用wx.checksession()來(lái)檢查wx.login(OBJECT) 是否過(guò)期的,因?yàn)橛脩粲锌赡苄薷牧祟^像、昵稱、城市,省份等信息,可以通過(guò)檢查wx.login(OBJECT) 是否過(guò)期來(lái)更新著些信息;
小程序的登錄狀態(tài)維護(hù)本質(zhì)就是維護(hù)session_key的時(shí)效性
這邊附上我的HttpUtils工具代碼,如果只要用get的話可以復(fù)制部分:
如果是直
/**
* HttpUtils工具類
*
* @author
*/
public class HttpUtils {
/**
* 請(qǐng)求方式:post
*/
public static String POST = "post";
/**
* 編碼格式:utf-8
*/
private static final String CHARSET_UTF_8 = "UTF-8";
/**
* 報(bào)文頭部json
*/
private static final String APPLICATION_JSON = "application/json";
/**
* 請(qǐng)求超時(shí)時(shí)間
*/
private static final int CONNECT_TIMEOUT = 60 * 1000;
/**
* 傳輸超時(shí)時(shí)間
*/
private static final int SO_TIMEOUT = 60 * 1000;
/**
* 日志
*/
private static final Logger logger = LoggerFactory.getLogger(HttpUtils.class);
/**
* @param protocol
* @param url
* @param paraMap
* @return
* @throws Exception
*/
public static String doPost(String protocol, String url,
Map<String, Object> paraMap) throws Exception {
CloseableHttpClient httpClient = null;
CloseableHttpResponse resp = null;
String rtnValue = null;
try {
if (protocol.equals("http")) {
httpClient = HttpClients.createDefault();
} else {
// 獲取https安全客戶端
httpClient = HttpUtils.getHttpsClient();
}
HttpPost httpPost = new HttpPost(url);
List<NameValuePair> list = msgs2valuePairs(paraMap);
// List<NameValuePair> list = new ArrayList<NameValuePair>();
// if (null != paraMap &¶Map.size() > 0) {
// for (Entry<String, Object> entry : paraMap.entrySet()) {
// list.add(new BasicNameValuePair(entry.getKey(), entry
// .getValue().toString()));
// }
// }
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(SO_TIMEOUT)
.setConnectTimeout(CONNECT_TIMEOUT).build();// 設(shè)置請(qǐng)求和傳輸超時(shí)時(shí)間
httpPost.setConfig(requestConfig);
httpPost.setEntity(new UrlEncodedFormEntity(list, CHARSET_UTF_8));
resp = httpClient.execute(httpPost);
rtnValue = EntityUtils.toString(resp.getEntity(), CHARSET_UTF_8);
} catch (Exception e) {
logger.error(e.getMessage());
throw e;
} finally {
if (null != resp) {
resp.close();
}
if (null != httpClient) {
httpClient.close();
}
}
return rtnValue;
}
/**
* 獲取https,單向驗(yàn)證
*
* @return
* @throws Exception
*/
public static CloseableHttpClient getHttpsClient() throws Exception {
try {
TrustManager[] trustManagers = new TrustManager[]{new X509TrustManager() {
public void checkClientTrusted(
X509Certificate[] paramArrayOfX509Certificate,
String paramString) throws CertificateException {
}
public void checkServerTrusted(
X509Certificate[] paramArrayOfX509Certificate,
String paramString) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}};
SSLContext sslContext = SSLContext
.getInstance(SSLConnectionSocketFactory.TLS);
sslContext.init(new KeyManager[0], trustManagers,
new SecureRandom());
SSLContext.setDefault(sslContext);
sslContext.init(null, trustManagers, null);
SSLConnectionSocketFactory connectionSocketFactory = new SSLConnectionSocketFactory(
sslContext,
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
HttpClientBuilder clientBuilder = HttpClients.custom()
.setSSLSocketFactory(connectionSocketFactory);
clientBuilder.setRedirectStrategy(new LaxRedirectStrategy());
CloseableHttpClient httpClient = clientBuilder.build();
return httpClient;
} catch (Exception e) {
throw new Exception("http client 遠(yuǎn)程連接失敗", e);
}
}
/**
* post請(qǐng)求
*
* @param msgs
* @param url
* @return
* @throws ClientProtocolException
* @throws UnknownHostException
* @throws IOException
*/
public static String post(Map<String, Object> msgs, String url)
throws ClientProtocolException, UnknownHostException, IOException {
CloseableHttpClient httpClient = HttpClients.createDefault();
try {
HttpPost request = new HttpPost(url);
List<NameValuePair> valuePairs = msgs2valuePairs(msgs);
// List<NameValuePair> valuePairs = new ArrayList<NameValuePair>();
// if (null != msgs) {
// for (Entry<String, Object> entry : msgs.entrySet()) {
// if (entry.getValue() != null) {
// valuePairs.add(new BasicNameValuePair(entry.getKey(),
// entry.getValue().toString()));
// }
// }
// }
request.setEntity(new UrlEncodedFormEntity(valuePairs, CHARSET_UTF_8));
CloseableHttpResponse resp = httpClient.execute(request);
return EntityUtils.toString(resp.getEntity(), CHARSET_UTF_8);
} finally {
httpClient.close();
}
}
/**
* post請(qǐng)求
*
* @param msgs
* @param url
* @return
* @throws ClientProtocolException
* @throws UnknownHostException
* @throws IOException
*/
public static byte[] postGetByte(Map<String, Object> msgs, String url)
throws ClientProtocolException, UnknownHostException, IOException {
CloseableHttpClient httpClient = HttpClients.createDefault();
InputStream inputStream = null;
byte[] data = null;
try {
HttpPost request = new HttpPost(url);
List<NameValuePair> valuePairs = msgs2valuePairs(msgs);
// List<NameValuePair> valuePairs = new ArrayList<NameValuePair>();
// if (null != msgs) {
// for (Entry<String, Object> entry : msgs.entrySet()) {
// if (entry.getValue() != null) {
// valuePairs.add(new BasicNameValuePair(entry.getKey(),
// entry.getValue().toString()));
// }
// }
// }
request.setEntity(new UrlEncodedFormEntity(valuePairs, CHARSET_UTF_8));
CloseableHttpResponse response = httpClient.execute(request);
try {
// 獲取相應(yīng)實(shí)體
HttpEntity entity = response.getEntity();
if (entity != null) {
inputStream = entity.getContent();
data = readInputStream(inputStream);
}
return data;
} catch (Exception e) {
e.printStackTrace();
} finally {
httpClient.close();
return null;
}
} finally {
httpClient.close();
}
}
/** 將流 保存為數(shù)據(jù)數(shù)組
* @param inStream
* @return
* @throws Exception
*/
public static byte[] readInputStream(InputStream inStream) throws Exception {
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
// 創(chuàng)建一個(gè)Buffer字符串
byte[] buffer = new byte[1024];
// 每次讀取的字符串長(zhǎng)度,如果為-1,代表全部讀取完畢
int len = 0;
// 使用一個(gè)輸入流從buffer里把數(shù)據(jù)讀取出來(lái)
while ((len = inStream.read(buffer)) != -1) {
// 用輸出流往buffer里寫(xiě)入數(shù)據(jù),中間參數(shù)代表從哪個(gè)位置開(kāi)始讀,len代表讀取的長(zhǎng)度
outStream.write(buffer, 0, len);
}
// 關(guān)閉輸入流
inStream.close();
// 把outStream里的數(shù)據(jù)寫(xiě)入內(nèi)存
return outStream.toByteArray();
}
/**
* get請(qǐng)求
*
* @param msgs
* @param url
* @return
* @throws ClientProtocolException
* @throws UnknownHostException
* @throws IOException
*/
public static String get(Map<String, Object> msgs, String url)
throws ClientProtocolException, UnknownHostException, IOException {
CloseableHttpClient httpClient = HttpClients.createDefault();
try {
List<NameValuePair> valuePairs = msgs2valuePairs(msgs);
// List<NameValuePair> valuePairs = new ArrayList<NameValuePair>();
// if (null != msgs) {
// for (Entry<String, Object> entry : msgs.entrySet()) {
// if (entry.getValue() != null) {
// valuePairs.add(new BasicNameValuePair(entry.getKey(),
// entry.getValue().toString()));
// }
// }
// }
// EntityUtils.toString(new UrlEncodedFormEntity(valuePairs),
// CHARSET);
url = url + "?" + URLEncodedUtils.format(valuePairs, CHARSET_UTF_8);
HttpGet request = new HttpGet(url);
CloseableHttpResponse resp = httpClient.execute(request);
return EntityUtils.toString(resp.getEntity(), CHARSET_UTF_8);
} finally {
httpClient.close();
}
}
public static <T> T post(Map<String, Object> msgs, String url,
Class<T> clazz) throws ClientProtocolException,
UnknownHostException, IOException {
String json = HttpUtils.post(msgs, url);
T t = JSON.parseObject(json, clazz);
return t;
}
public static <T> T get(Map<String, Object> msgs, String url, Class<T> clazz)
throws ClientProtocolException, UnknownHostException, IOException {
String json = HttpUtils.get(msgs, url);
T t = JSON.parseObject(json, clazz);
return t;
}
public static String postWithJson(Map<String, Object> msgs, String url)
throws ClientProtocolException, IOException {
CloseableHttpClient httpClient = HttpClients.createDefault();
try {
String jsonParam = JSON.toJSONString(msgs);
HttpPost post = new HttpPost(url);
post.setHeader("Content-Type", APPLICATION_JSON);
post.setEntity(new StringEntity(jsonParam, CHARSET_UTF_8));
CloseableHttpResponse response = httpClient.execute(post);
return new String(EntityUtils.toString(response.getEntity()).getBytes("iso8859-1"), CHARSET_UTF_8);
} finally {
httpClient.close();
}
}
public static <T> T postWithJson(Map<String, Object> msgs, String url, Class<T> clazz) throws ClientProtocolException,
UnknownHostException, IOException {
String json = HttpUtils.postWithJson(msgs, url);
T t = JSON.parseObject(json, clazz);
return t;
}
public static List<NameValuePair> msgs2valuePairs(Map<String, Object> msgs) {
List<NameValuePair> valuePairs = new ArrayList<NameValuePair>();
if (null != msgs) {
for (Entry<String, Object> entry : msgs.entrySet()) {
if (entry.getValue() != null) {
valuePairs.add(new BasicNameValuePair(entry.getKey(),
entry.getValue().toString()));
}
}
}
return valuePairs;
}
}
接傳session_key到前端的,下面的可以不用看了.
如果是redis存的sesssion_key的token的話,這邊附上登陸的時(shí)候的token轉(zhuǎn)換為session_key.
自定義攔截器注解:
import java.lang.annotation.*;
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AuthIgnore {
}
攔截器部分代碼:
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
AuthIgnore annotation;
if(handler instanceof HandlerMethod) {
annotation = ((HandlerMethod) handler).getMethodAnnotation(AuthIgnore.class);
}else{
return true;
}
//如果有@AuthIgnore注解,則不驗(yàn)證token
if(annotation != null){
return true;
}
// //獲取微信access_token;
// if(!redisUtil.exists(Constants.ACCESS_TOKEN)){
// Map<String, Object> msgs = new HashMap<>();
// msgs.put("appid",appId);
// msgs.put("secret",secret);
// msgs.put("grant_type","client_credential");
// String data = HttpUtils.get(msgs,Constants.GETACCESSTOKEN); // java的網(wǎng)絡(luò)請(qǐng)求,返回字符串
// String errcode= JSONObject.parseObject(data).getString("errcode");
// String errmsg= JSONObject.parseObject(data).getString("errmsg");
// if(StringUtils.isBlank(errcode)){
// //存儲(chǔ)access_token
// String access_token= JSONObject.parseObject(data).getString("access_token");
// long expires_in=Long.parseLong(JSONObject.parseObject(data).getString("expires_in"));
// redisUtil.setex("ACCESS_TOKEN",access_token, expires_in);
// }else{
// //異常返回?cái)?shù)據(jù)攔截,返回json數(shù)據(jù)
// response.setCharacterEncoding("UTF-8");
// response.setContentType("application/json; charset=utf-8");
// PrintWriter out = response.getWriter();
// ResponseBean<Object> responseBean=new ResponseBean<>(false,null, errmsg);
// out = response.getWriter();
// out.append(JSON.toJSON(responseBean).toString());
// return false;
// }
// }
//獲取用戶憑證
String token = request.getHeader(Constants.USER_TOKEN);
// if(StringUtils.isBlank(token)){
// token = request.getParameter(Constants.USER_TOKEN);
// }
// if(StringUtils.isBlank(token)){
// Object obj = request.getAttribute(Constants.USER_TOKEN);
// if(null!=obj){
// token=obj.toString();
// }
// }
// //token憑證為空
// if(StringUtils.isBlank(token)){
// //token不存在攔截,返回json數(shù)據(jù)
// response.setCharacterEncoding("UTF-8");
// response.setContentType("application/json; charset=utf-8");
// PrintWriter out = response.getWriter();
// try{
// ResponseBean<Object> responseBean=new ResponseBean<>(false,null, UnicomResponseEnums.TOKEN_EMPTY);
// out = response.getWriter();
// out.append(JSON.toJSON(responseBean).toString());
// return false;
// }
// catch (Exception e) {
// e.printStackTrace();
// response.sendError(500);
// return false;
// }
// }
if(token==null||!redisUtil.exists(token)){
//用戶未登錄,返回json數(shù)據(jù)
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json; charset=utf-8");
PrintWriter out = response.getWriter();
try{
ResponseBean<Object> responseBean=new ResponseBean<>(false,null, UnicomResponseEnums.DIS_LOGIN);
out = response.getWriter();
out.append(JSON.toJSON(responseBean).toString());
return false;
}
catch (Exception e) {
e.printStackTrace();
response.sendError(500);
return false;
}
}
tokenManager.refreshUserToken(token);
return true;
}
}
過(guò)濾器配置:
@Configuration
public class InterceptorConfig extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(authorizationInterceptor())
.addPathPatterns("/**")// 攔截所有請(qǐng)求,通過(guò)判斷是否有 @AuthIgnore注解 決定是否需要登錄
.excludePathPatterns("/user/login");//排除登錄
}
@Bean
public AuthorizationInterceptor authorizationInterceptor() {
return new AuthorizationInterceptor();
}
}
token管理:
@Service
public class TokenManager {
@Resource
private RedisUtil redisUtil;
//生成token(格式為token:設(shè)備-加密的用戶名-時(shí)間-六位隨機(jī)數(shù))
public String generateToken(String userAgentStr, String username) {
StringBuilder token = new StringBuilder("token:");
//設(shè)備
UserAgent userAgent = UserAgent.parseUserAgentString(userAgentStr);
if (userAgent.getOperatingSystem().isMobileDevice()) {
token.append("MOBILE-");
} else {
token.append("PC-");
}
//加密的用戶名
token.append(MD5Utils.MD5Encode(username) + "-");
//時(shí)間
token.append(new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date()) + "-");
//六位隨機(jī)字符串
token.append(UUID.randomUUID().toString());
System.out.println("token-->" + token.toString());
return token.toString();
}
/**
* 登錄用戶,創(chuàng)建token
*
* @param token
*/
//把token存到redis中
public void save(String token, Users user) {
if (token.startsWith("token:PC")) {
redisUtil.setex(token,JSON.toJSONString(user), Constants.TOKEN_EX);
} else {
redisUtil.setex(token,JSON.toJSONString(user), Constants.TOKEN_EX);
}
}
/**
* 刷新用戶
*
* @param token
*/
public void refreshUserToken(String token) {
if (redisUtil.exists(token)) {
String value=redisUtil.get(token);
redisUtil.setex(token, value, Constants.TOKEN_EX);
}
}
/**
* 用戶退出登陸
*
* @param token
*/
public void loginOut(String token) {
redisUtil.remove(token);
}
/**
* 獲取用戶信息
*
* @param token
* @return
*/
public Users getUserInfoByToken(String token) {
if (redisUtil.exists(token)) {
String jsonString = redisUtil.get(token);
Users user =JSONObject.parseObject(jsonString, Users.class);
return user;
}
return null;
}
}
redis工具類:
@Component
public class RedisUtil {
@Resource
private RedisTemplate<String, String> redisTemplate;
public void set(String key, String value) {
ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
valueOperations.set(key, value);
}
public void setex(String key, String value, long seconds) {
ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
valueOperations.set(key, value, seconds,TimeUnit.SECONDS);
}
public Boolean exists(String key) {
return redisTemplate.hasKey(key);
}
public void remove(String key) {
redisTemplate.delete(key);
}
public String get(String key) {
ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
return valueOperations.get(key);
}
}
最后redis要實(shí)現(xiàn)序列化,序列化最終的目的是為了對(duì)象可以跨平臺(tái)存儲(chǔ),和進(jìn)行網(wǎng)絡(luò)傳輸。而我們進(jìn)行跨平臺(tái)存儲(chǔ)和網(wǎng)絡(luò)傳輸?shù)姆绞骄褪荌O,而我們的IO支持的數(shù)據(jù)格式就是字節(jié)數(shù)組。本質(zhì)上存儲(chǔ)和網(wǎng)絡(luò)傳輸 都需要經(jīng)過(guò) 把一個(gè)對(duì)象狀態(tài)保存成一種跨平臺(tái)識(shí)別的字節(jié)格式,然后其他的平臺(tái)才可以通過(guò)字節(jié)信息解析還原對(duì)象信息。
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
public class RedisConfig {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
//使用fastjson序列化
FastJsonRedisSerializer fastJsonRedisSerializer = new FastJsonRedisSerializer(Object.class);
// value值的序列化采用fastJsonRedisSerializer
template.setValueSerializer(fastJsonRedisSerializer);
template.setHashValueSerializer(fastJsonRedisSerializer);
// key的序列化采用StringRedisSerializer
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean(StringRedisTemplate.class)
public StringRedisTemplate stringRedisTemplate(
RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
作者:gigass
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請(qǐng)注明出處。
總結(jié)
到此這篇關(guān)于基于redis的小程序登錄實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)redis小程序登錄內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
javaScript手機(jī)號(hào)碼校驗(yàn)工具類PhoneUtils詳解
這篇文章主要為大家詳細(xì)介紹了javaScript手機(jī)號(hào)碼校驗(yàn)工具類PhoneUtils,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12
JavaScript+css+HTML實(shí)現(xiàn)移動(dòng)端輪播圖(含源碼)
這篇文章主要介紹了JavaScript+css+HTML實(shí)現(xiàn)移動(dòng)端輪播圖并含源碼的分享,需要的小伙伴可以參考一下,希望對(duì)你有所幫助2022-01-01
js如何判斷訪問(wèn)是來(lái)自搜索引擎(蜘蛛人)還是直接訪問(wèn)
這篇文章主要介紹了js如何判斷訪問(wèn)是來(lái)自搜索引擎(蜘蛛人)還是直接訪問(wèn),需要的朋友可以參考下2015-09-09
Uncaught?SyntaxError:Unexpected?token?'<'?(
這篇文章主要為大家介紹了JS判斷趨近于直線的多邊形(退化多邊形)實(shí)例探究,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01
不到200行 JavaScript 代碼實(shí)現(xiàn)富文本編輯器的方法
這篇文章主要介紹了不到200行 JavaScript 代碼實(shí)現(xiàn)富文本編輯器的方法,需要的朋友可以參考下2018-01-01
javascript實(shí)現(xiàn)支付寶滑塊驗(yàn)證碼效果
這篇文章主要為大家詳細(xì)介紹了javascript實(shí)現(xiàn)支付寶滑塊驗(yàn)證碼效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-07-07
javascript實(shí)現(xiàn)類似百度分享功能的方法
這篇文章主要介紹了javascript實(shí)現(xiàn)類似百度分享功能的方法,以實(shí)例形式較為完整的分析了基于javascript實(shí)現(xiàn)百度分享功能所涉及的樣式與分享功能實(shí)現(xiàn)方法,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-07-07

