以下是整合后的完整方案,结合了 UNI-APP 前端 和 Java Spring Boot 后端,实现小程序手机号登录功能:
1. 前端实现:获取用户手机号并调用登录接口
在 UNI-APP 中,使用 button 组件获取用户的手机号授权,并将授权后的 code 发送到后端登录接口。
1.1 前端代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| <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 中添加以下依赖:
1 2 3 4 5 6 7 8 9 10 11 12
| <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:
1 2
| wechat.appid=YOUR_APPID wechat.secret=YOUR_APPSECRET
|
2.3 实现获取 access_token 和手机号
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| 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();
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 实现登录逻辑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| 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); }
String token = "token_" + user.getUserId();
return new ApiResponse(true, "登录成功", new LoginResponse(user, token)); } }
class User { private String userId; private String phoneNumber;
}
@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;
}
class LoginResponse { private User user; private String token;
}
class ApiResponse { private boolean success; private String message; private Object data;
}
|
3. 安全性注意事项
-
code 的有效性:确保 code 是一次性的,且及时使用。
-
access_token 缓存:避免频繁请求微信 API,缓存 access_token。
- HTTPS:确保接口使用 HTTPS 加密传输。
- 用户隐私:手机号是敏感信息,确保存储和传输过程中加密。
4. 总结
通过以上步骤,你可以实现以下功能:
- 前端获取用户手机号授权,发送
code 到后端。
- 后端通过
code 获取手机号,完成登录逻辑。
- 前端保存登录状态,并在需要时校验登录状态。
这种方案既符合微信小程序的规范,又能满足常见的登录需求。