以下是整合后的完整方案,结合了 UNI-APP 前端 和 Java Spring Boot 后端,实现小程序手机号登录功能:
1. 前端实现:获取用户手机号并调用登录接口
在 UNI-APP 中,使用 button
组件获取用户的手机号授权,并将授权后的 code
发送到后端登录接口。
1.1 前端代码
<template>
<view>
<button open-type="getPhoneNumber" @getphonenumber="getPhoneNumber">手机号登录</button>
</view>
</template>
<script>
export default {
methods: {
async getPhoneNumber(e) {
if (e.detail.errMsg === 'getPhoneNumber:ok') {
// 用户点击了允许授权
const { code } = e.detail;
// 调用后端登录接口
const res = await uni.request({
url: 'https://your-server.com/api/loginByPhone', // 替换为你的后端地址
method: 'POST',
data: { code },
});
if (res.data.success) {
// 登录成功,保存用户信息和 token
uni.setStorageSync('userInfo', res.data.data.user);
uni.setStorageSync('token', res.data.data.token);
uni.showToast({ title: '登录成功', icon: 'success' });
uni.navigateTo({ url: '/pages/home/index' }); // 跳转到首页
} else {
uni.showToast({ title: '登录失败', icon: 'none' });
}
} else {
// 用户点击了拒绝授权
uni.showToast({ title: '用户拒绝授权', icon: 'none' });
}
}
}
}
</script>
2. 后端实现:Spring Boot 处理登录逻辑
后端需要完成以下任务:
- 通过
code
获取用户的手机号。 - 根据手机号查询用户是否存在。
- 如果用户不存在,则创建新用户。
- 返回用户信息和登录凭证(如
token
)。
2.1 添加依赖
在 pom.xml
中添加以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
2.2 配置微信小程序信息
在 application.properties
中配置微信小程序的 appid
和 secret
:
wechat.appid=YOUR_APPID
wechat.secret=YOUR_APPSECRET
2.3 实现获取 access_token
和手机号
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
@Service
public class WechatService {
@Value("${wechat.appid}")
private String appid;
@Value("${wechat.secret}")
private String secret;
private final ObjectMapper objectMapper = new ObjectMapper();
// 获取 access_token
public String getAccessToken() throws Exception {
String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appid + "&secret=" + secret;
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpGet request = new HttpGet(url);
String response = EntityUtils.toString(httpClient.execute(request).getEntity());
JsonNode jsonNode = objectMapper.readTree(response);
return jsonNode.get("access_token").asText();
}
}
// 获取用户手机号
public String getPhoneNumber(String code) throws Exception {
String accessToken = getAccessToken();
String url = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token=" + accessToken;
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
HttpPost request = new HttpPost(url);
request.setHeader("Content-Type", "application/json");
String jsonBody = "{\"code\":\"" + code + "\"}";
request.setEntity(new StringEntity(jsonBody));
String response = EntityUtils.toString(httpClient.execute(request).getEntity());
JsonNode jsonNode = objectMapper.readTree(response);
return jsonNode.path("phone_info").path("phoneNumber").asText();
}
}
}
2.4 实现登录逻辑
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
public class LoginController {
@Autowired
private WechatService wechatService;
@Autowired
private UserService userService;
@PostMapping("/api/loginByPhone")
public ApiResponse loginByPhone(@RequestBody LoginRequest request) throws Exception {
String code = request.getCode();
String phoneNumber = wechatService.getPhoneNumber(code);
// 查询用户是否存在
User user = userService.findUserByPhoneNumber(phoneNumber);
if (user == null) {
// 如果用户不存在,创建新用户
user = userService.createUser(phoneNumber);
}
// 生成 token(这里简单模拟)
String token = "token_" + user.getUserId();
// 返回用户信息和 token
return new ApiResponse(true, "登录成功", new LoginResponse(user, token));
}
}
// 用户实体类
class User {
private String userId;
private String phoneNumber;
// Getters and Setters
}
// 用户服务类
@Service
class UserService {
private final List<User> users = new ArrayList<>();
public User findUserByPhoneNumber(String phoneNumber) {
return users.stream()
.filter(user -> user.getPhoneNumber().equals(phoneNumber))
.findFirst()
.orElse(null);
}
public User createUser(String phoneNumber) {
User user = new User();
user.setUserId(String.valueOf(System.currentTimeMillis()));
user.setPhoneNumber(phoneNumber);
users.add(user);
return user;
}
}
// 请求和响应类
class LoginRequest {
private String code;
// Getters and Setters
}
class LoginResponse {
private User user;
private String token;
// Getters and Setters
}
class ApiResponse {
private boolean success;
private String message;
private Object data;
// Getters and Setters
}
3. 安全性注意事项
-
code
的有效性:确保code
是一次性的,且及时使用。 -
access_token
缓存:避免频繁请求微信 API,缓存access_token
。 - HTTPS:确保接口使用 HTTPS 加密传输。
- 用户隐私:手机号是敏感信息,确保存储和传输过程中加密。
4. 总结
通过以上步骤,你可以实现以下功能:
- 前端获取用户手机号授权,发送
code
到后端。 - 后端通过
code
获取手机号,完成登录逻辑。 - 前端保存登录状态,并在需要时校验登录状态。
这种方案既符合微信小程序的规范,又能满足常见的登录需求。