知识点
openid:相当于一个微信用户的身份证,唯一标识一个微信用户。
code:前端通过微信提供的API wx.login({})得到的一个code,可以通过这个code获取openid和sessionkey。
token:令牌的意思,是服务端生成的一串字符串,作为客户端进行请求的一个标识,token可以是任何东西,只要能唯一标识你的用户就行,但尽量不要单纯用openid,不够安全,本次例子直接用uuid生成。
uuid:就是一个不会重复的值,通常可以作为主键,还是不懂可以百度一下
rawData:微信用户的基础信息,包括昵称之类的信息。
流程
首先大概讲一下流程吧,首先是后端获取前端传过来的rawData和code。然后再利用code获得openid存入用户表里,接着就利用uuid生成token,再返回token回前端,以后前端每次调用请求时就在请求头加上token,只要后端识别到相应的token就知道这是那一个用户。
redis的作用
token作为用户登陆的令牌,并且作为缓存的key值,存入到缓存中,方便后续直接从缓存中更快速的获取用户信息,而不用从数据库中获取。
- 首先是pom.xml。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--引入mybastis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.1</version>
</dependency>
<!--引入mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--redis的引入-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!--SHA1 加密工具包-->
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.11</version>
</dependency>
<!--Apache 工具包-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8.1</version>
</dependency>
<!--C3P0-->
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
<!--解析 JSON 工具-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.33</version>
</dependency>
<!--调用 HTTP 请求-->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.8.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!--逆向工程的配置-->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<configuration>
<configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
<verbose>true</verbose>
<overwrite>true</overwrite>
</configuration>
</plugin>
</plugins>
</build>
</project>
- 通过code获取openid的代码
public SessionDTO jscode2session(String code) {
String url = "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code";
OkHttpClient okHttpClient = new OkHttpClient();
Request request = new Request.Builder()
.addHeader("content-type", "application/json")
.url(String.format(url, appid, secret, code))
.build();
try {
Response execute = okHttpClient.newCall(request).execute();
if (execute.isSuccessful()) {
//获取openid等信息
SessionDTO sessionDTO = JSON.parseObject(execute.body().string(), SessionDTO.class);
return sessionDTO;
} else {
throw new ErrorCodeException(CommonErrorCode.OBTAIN_OPENID_ERROR);
}
} catch (IOException e) {
throw new ErrorCodeException(CommonErrorCode.OBTAIN_OPENID_ERROR);
}
}
3.redis的配置(设置了序列化和反序列化,方便存储值为对象的键值对,记得值非string时不要使用@Cacheable注解,自己手写)
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 使用Jackson2JsonRedisSerialize 替换默认序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
// 设置value的序列化规则和 key的序列化规则
redisTemplate.setKeySerializer(new StringRedisSerializer());
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
- redis的使用
//插入到数据库时顺便插入到缓存中
@Override
public User insertSelective(User record) {
int flag=this.userMapper.insertSelective(record);
if (flag==0){
return null;
}
//插入成功就放入缓存
stringRedisTemplate.opsForValue().set(record.getToken(),record);
//这里是设置键的有效日期,不设置就是永久的token
// stringRedisTemplate.expire(record.getToken(), 123L, TimeUnit.SECONDS);
return record;
}
- redis的测试
@Test
public void test(){
ValueOperations operations=stringRedisTemplate.opsForValue();
// 通过 token 从数据库中获取信息,如果没有验证失败
// 如果通过一台设备登录,再通过另一台设备登录,第一台设备会自动登出
// 此处已经将user放到缓存中,key值为token
User openid=(User) operations.get("改成你的token值");
System.out.println(openid.getToken());
}
- contoller的编写
@RequestMapping("api/login")
public ResultDTO login(@RequestBody LoginDTO loginDTO){
try{
//获取openid和session_key
SessionDTO sessionDTO=wechatAdapter.jscode2session(loginDTO.getCode());
//检验传过来的数据是否已被篡改
// DigestUtil.checkDigest(loginDTO.getRawData(),loginDTO.getCode(),loginDTO.getSignature());
//获取数据
User user= JSON.parseObject(loginDTO.getRawData(),User.class);
//生成用户个人的token
String token= UUID.randomUUID().toString();
Date date = new Date();
//将user传到数据库中
user.setToken(token);
user.setUid(DateUtil.change_str(date));
userService.insertSelective(user);
TokenDTO data = new TokenDTO();
data.setToken(token);
return ResultDTO.ok(data);
}catch (ErrorCodeException e){
return ResultDTO.fail(e);
}catch (Exception e){
System.out.println(e.toString());
return ResultDTO.fail(CommonErrorCode.UNKOWN_ERROR);
}
}
项目源码
服务器端地址:https://github.com/jiaojiaoyow/wx_login.git (懒得写小程序端,就直接用其他人的了)
小程序端:https://github.com/codedrinker/jiuask
使用注意点,记得去application中更改appid和secret,generatorConfig.xml是逆向工程的,要使用的话得先配置一下(具体可以百度一下),还有拦截器一般是无法注入得,需要改配置(源码已经加了,做个提醒)。
@Bean
public HandlerInterceptor getMyInterceptor(){
return new LoginInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(getMyInterceptor());
}
有什么问题欢迎留言哦,下一期微信做什么功能,也可以提出你的意见,当然,不是特别专业,有不好得地方请见谅,提一下你的意见给我