Mini program implements WeChat login Java backend (2) - backend implementation

Table of contents

I. Overview

2. Back-end code

1. Dependence

2. Tools

(1) HttpClientUtil

(2) R

(3) StatusCode

3. Front-end request to receive object 

4. Service layer

5. Control layer

3. Test

4. Summary


I. Overview

After writing the front-end of the WeChat login applet, implement the back-end logic.

The backend mainly has two interfaces:

  • /wxlogin is responsible for processing login logic, such as returning the user token and automatically registering the user for the first time.
  • /checkwxlogin is responsible for processing login verification and checking whether the current user is logged in.

2. Back-end code

1. Dependence

The core dependencies of the project are as follows:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- http请求工具包依赖 -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.2</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.75</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
            <version>2.7.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

2. Tools

(1) HttpClientUtil

HttpClientUtil is a tool class for calling WeChat external interface. I see that most others write this way, but I personally feel that if it is a small project, or if the project rarely calls external APIs, direct string splicing seems to be more efficient. Encapsulation into a tool class should be to ensure readability, maintainability and security.

public class HttpClientUtil {

    final static int TIMEOUT = 1000;

    final static int TIMEOUT_MSEC = 5 * 1000;

    public static String doPost(String url, Map<String, String> paramMap) throws IOException {
        // 创建Httpclient对象
        CloseableHttpClient httpClient = HttpClients.createDefault();
        //创建响应对象
        CloseableHttpResponse response = null;
        String resultString = "";
        try {
            // 创建Http Post请求
            HttpPost httpPost = new HttpPost(url);
            // 创建参数列表
            if (paramMap != null) {
                List<NameValuePair> paramList = new ArrayList<>();
                for (Map.Entry<String, String> param : paramMap.entrySet()) {
                    paramList.add(new BasicNameValuePair(param.getKey(), param.getValue()));
                }
                // 模拟表单
                UrlEncodedFormEntity entity = new UrlEncodedFormEntity(paramList);
                httpPost.setEntity(entity);
            }
            httpPost.setConfig(builderRequestConfig());
            // 执行请求
            response = httpClient.execute(httpPost);

            resultString = EntityUtils.toString(response.getEntity(), "UTF-8");
        } catch (Exception e) {
            throw new IOException("HttpClientUtil工具类执行请求失败---" + e);
        } finally {
            try {
                response.close();
            } catch (IOException e) {
                throw new IOException("HttpClientUtil工具类关闭响应资源失败---" + e);
            }
        }
        return resultString;
    }

    private static RequestConfig builderRequestConfig() {
        return RequestConfig.custom()
                .setConnectTimeout(TIMEOUT_MSEC)
                .setConnectionRequestTimeout(TIMEOUT_MSEC)
                .setSocketTimeout(TIMEOUT_MSEC).build();
    }
}

(2) R<T>

R<T> is a custom response type.

@Getter
@Setter
public class R<T> {
    private Integer code;
    private String msg;
    private T data;
    public R(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }
    public R(StatusCode statusCode) {
        this.code = statusCode.getCode();
        this.msg = statusCode.getMsg();
    }
    public R(Integer code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

(3) StatusCode

StatusCode is a custom response status code.

public enum StatusCode {
    Success(0,"成功"),
    Fail(-1,"失败"),
    InvalidParams(201,"非法参数"),
    InvalidGrantType(202,"非法授权");

    private Integer code;
  
    private String msg;
  
    StatusCode(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

3. Front-end request to receive object 

@Getter
@Setter
public class WechatLoginRequestDTO {
    @NotNull(message = "code不能为空")
    private String code;  //微信code
}

4. Service layer

interface:

public interface WechatService {
    String getToken(WechatLoginRequestDTO loginRequest) throws Exception;

    void checkToken(String token) throws Exception;
}

accomplish:

Note that the token here is generated using timestamp and SHA-256 hashing algorithm. Mainly to demonstrate the WeChat login process, the token is simply stored in redis. There is no logic for storing the database, and there is no registration process for first login. Readers can add it by themselves.

@Service
public class WechatServiceImpl implements WechatService {
    private static final String REQUEST_URL = "https://api.weixin.qq.com/sns/jscode2session";
    private static final String APPID = "xxxx"; //前面获取的appid
    private static final String SECRET = "xxxx"; //前面获取的小程序密钥,注意要和appid同属于一个项目
    private static final String GRANT_TYPE = "authorization_code";

    @Resource
    private RedisTemplate redisTemplate;

    @Override
    public String getToken(WechatLoginRequestDTO loginRequest) throws Exception {
        JSONObject sessionKeyOpenId = getSessionKeyOrOpenId(loginRequest.getCode());
        System.out.println("微信返还的openid"+sessionKeyOpenId);
        // 获取openId && sessionKey
        String openId = sessionKeyOpenId.getString("openid");
        //String sessionKey = sessionKeyOpenId.getString("session_key");
        // 获取当前时间戳
        long timestamp = Instant.now().getEpochSecond();
        // 拼接openid和时间戳
        String tokenData = openId + timestamp;
        // 使用SHA-256哈希算法对数据进行加密
        String token = getSHA256(tokenData);
        redisTemplate.opsForValue().set(token, 1);

        return token;
    }

    @Override
    public void checkToken(String token) throws Exception {
        if(redisTemplate.opsForValue().get(token)==null){
            throw new Exception("用户令牌不存在");
        }
    }

    //调用微信接口服务获取openId && sessionKey
    private JSONObject getSessionKeyOrOpenId(String code) throws Exception {
        Map<String, String> requestUrlParam = new HashMap<>();
        // 小程序appId,自己补充
        requestUrlParam.put("appid", APPID);
        // 小程序secret,自己补充
        requestUrlParam.put("secret", SECRET);
        // 小程序端返回的code
        requestUrlParam.put("js_code", code);
        // 默认参数
        requestUrlParam.put("grant_type", GRANT_TYPE);
        // post请求读取调用微信接口获取openid用户唯一标识
        String result = HttpClientUtil.doPost(REQUEST_URL, requestUrlParam);
        return JSON.parseObject(result);
    }

    private static String getSHA256(String data) throws NoSuchAlgorithmException {
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        byte[] hash = digest.digest(data.getBytes());
        StringBuilder hexString = new StringBuilder();

        for (byte b : hash) {
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) {
                hexString.append('0');
            }
            hexString.append(hex);
        }
        return hexString.toString();
    }
}

5. Control layer

@RestController
public class LoginController {

    @Resource
    WechatService wechatService;

    @PostMapping("/wxlogin")
    public R login(@Valid @RequestBody WechatLoginRequestDTO loginRequest) {
        System.out.println("收到微信code:"+loginRequest.getCode());
        R response = new R(StatusCode.Success);
        try {
            String token = wechatService.getToken(loginRequest);
            response.setData(token);
        } catch (Exception e) {
            response = new R(StatusCode.Fail.getCode(), e.getMessage());
        }
        return response;
    }

    @GetMapping("/checkwxlogin")
    public R checkLogin(@NotNull @RequestParam String token) {
        System.out.println("收到待校验token:"+token);
        R response = new R(StatusCode.Success);
        try {
            wechatService.checkToken(token);
        } catch (Exception e) {
            response = new R(StatusCode.Fail.getCode(), e.getMessage());
        }
        return response;
    }
}

3. Test

Startup project 

 Front-end testing

When the front end logs in for the first time, is_login is false and prints out the newly obtained token;

 The backend has printed out relevant information, including openid and sessionKey obtained from the WeChat service interface.

When the front end logs in again, the token saved in the cache works and is_login is displayed as true.

Also check Storage and you can see that the token has been deposited.

Test success! ! !

4. Summary

Now we have successfully implemented the basic functions of WeChat login, including storing WeChat user information in the database, obtaining WeChat user avatar nicknames, etc. We will continue to introduce them when there is time.

To be continued…

Guess you like

Origin blog.csdn.net/m0_63080216/article/details/131991201
Recommended