Phone number microservice
It's a gRPC based microservice. It uses Aliyun service to send sms to a phone and the codes sent are stored in MySQL compatiable databases. It has two interfaces:
- SendSMS, send a random 6 digits code to the specified phone number.
- Valid, check if the code is right.
Get the package:
go get github.com/liulinhuai/phone-number-service/phonenumberservice
Implementation
-
No sending policy is implemented, such as limiting the number of codes sent to a phone number per day or total, as Aliyun already implements that and it's configable.
-
After a successful call of SendSMS, all the previous codes of the same phone number will be invalidated.
-
After Valid is called more than attemptLimit(default is 3) against the same phone number p, all the codes sent to p will be invalidated to prevent guessing the codes.
-
After a call to Valid returned true, the following requests with the same phone number and code will return false even the code is correct. This means a code can only be used one time.
The full proto definition
syntax = "proto3";
package phonenumberservice;
service PhoneNumberService {
rpc SendSMS(SendSMSRequest) returns (Empty) {}
rpc Valid(ValidRequest) returns (ValidResponse) {}
}
message Empty {}
message SendSMSRequest {
string phone_number = 1;
string signature = 2;
string template_code = 3;
}
message ValidRequest {
string phone_number = 1;
string code = 2;
}
message ValidResponse {
bool valid = 1;
}
Example usage
func Example() {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
defer cancel()
conn, err := grpc.DialContext(ctx, ":50511", grpc.WithInsecure(), grpc.WithBlock())
if err != nil {
fmt.Printf("Dial returned an error: %v\n", err)
return
}
c := pb.NewPhoneNumberServiceClient(conn)
p := "186xxxxxxxx"
req := &pb.SendSMSRequest{PhoneNumber: p, Signature: "xxx", TemplateCode: "xxx"}
if _, err := c.SendSMS(ctx, req); err != nil {
fmt.Printf("SendSMS returned error: %v\n", err)
return
}
resp, err := c.Valid(ctx, &pb.ValidRequest{PhoneNumber: p, Code: "123456"})
if err != nil {
fmt.Printf("Valid returned error: %v\n", err)
return
}
if !resp.Valid {
fmt.Printf("Valid returned false, want true\n")
}
}
Set up the service
-
Set up the database first.
Create the database:
CREATE DATABASE `PhoneNumberService` DEFAULT CHARACTER SET ascii COLLATE ascii_bin;
Create the tables by using phonenumberservice.sql, note it uses ascii character set.
-
Set the environment variables needed by this service:
Data_SOURCE_NAME
for connecting to a database, example:
root:password@tcp([192.168.1.100]:3306)/PhoneNumberService
ALIYUN_ACCESS_KEY_ID
and ALIYUN_ACCESS_KEY_SECRET
for sending sms.
Use this service
-
Clone this repository, and run the tests to make sure it works:
git clone git@github.com:liulinhuai/phone-number-service.git
cd phone-number-service
go test
Tests that send real sms are skipped, to test them, comment the skip statement:
// t.Skip("send real sms is skipped")
-
Clone this repository and modify it as needed or use it as a docker image:
docker pull liulinhuai/phone-number-service:v0.1.0
-
Run the image (replace the password, ip and 'xxx'):
docker run \
-e DATA_SOURCE_NAME='root:password@tcp([192.168.1.100]:3306)/PhoneNumberService' \
-e ALIYUN_ACCESS_KEY_ID=xxx \
-e ALIYUN_ACCESS_KEY_SECRET=xxx \
-p 50511:50511 phone-number-service