知识点

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值,存入到缓存中,方便后续直接从缓存中更快速的获取用户信息,而不用从数据库中获取。

  1. 首先是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>
  1. 通过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;
   }
  1. 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;
   }
  1. redis的测试
@Test
   public void test(){
       ValueOperations operations=stringRedisTemplate.opsForValue();
       // 通过 token 从数据库中获取信息,如果没有验证失败
       // 如果通过一台设备登录,再通过另一台设备登录,第一台设备会自动登出
       // 此处已经将user放到缓存中,key值为token
       User openid=(User) operations.get("改成你的token值");
       System.out.println(openid.getToken());
   }
  1. 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());

    }

有什么问题欢迎留言哦,下一期微信做什么功能,也可以提出你的意见,当然,不是特别专业,有不好得地方请见谅,提一下你的意见给我