ip白名单
This commit is contained in:
parent
c96777e405
commit
535ca29fa7
@ -1042,6 +1042,106 @@ func (x *RecordLoginBlockedResponse) GetRecorded() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// CheckLoginRiskIPWhitelistRequest 查询入口 IP 是否命中后台白名单。
|
||||
type CheckLoginRiskIPWhitelistRequest struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Meta *RequestMeta `protobuf:"bytes,1,opt,name=meta,proto3" json:"meta,omitempty"`
|
||||
ClientIp string `protobuf:"bytes,2,opt,name=client_ip,json=clientIp,proto3" json:"client_ip,omitempty"`
|
||||
}
|
||||
|
||||
func (x *CheckLoginRiskIPWhitelistRequest) Reset() {
|
||||
*x = CheckLoginRiskIPWhitelistRequest{}
|
||||
mi := &file_proto_user_v1_auth_proto_msgTypes[13]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *CheckLoginRiskIPWhitelistRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*CheckLoginRiskIPWhitelistRequest) ProtoMessage() {}
|
||||
|
||||
func (x *CheckLoginRiskIPWhitelistRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_user_v1_auth_proto_msgTypes[13]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use CheckLoginRiskIPWhitelistRequest.ProtoReflect.Descriptor instead.
|
||||
func (*CheckLoginRiskIPWhitelistRequest) Descriptor() ([]byte, []int) {
|
||||
return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{13}
|
||||
}
|
||||
|
||||
func (x *CheckLoginRiskIPWhitelistRequest) GetMeta() *RequestMeta {
|
||||
if x != nil {
|
||||
return x.Meta
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *CheckLoginRiskIPWhitelistRequest) GetClientIp() string {
|
||||
if x != nil {
|
||||
return x.ClientIp
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// CheckLoginRiskIPWhitelistResponse 返回入口 IP 是否跳过登录地区屏蔽。
|
||||
type CheckLoginRiskIPWhitelistResponse struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Whitelisted bool `protobuf:"varint,1,opt,name=whitelisted,proto3" json:"whitelisted,omitempty"`
|
||||
}
|
||||
|
||||
func (x *CheckLoginRiskIPWhitelistResponse) Reset() {
|
||||
*x = CheckLoginRiskIPWhitelistResponse{}
|
||||
mi := &file_proto_user_v1_auth_proto_msgTypes[14]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *CheckLoginRiskIPWhitelistResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*CheckLoginRiskIPWhitelistResponse) ProtoMessage() {}
|
||||
|
||||
func (x *CheckLoginRiskIPWhitelistResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_user_v1_auth_proto_msgTypes[14]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use CheckLoginRiskIPWhitelistResponse.ProtoReflect.Descriptor instead.
|
||||
func (*CheckLoginRiskIPWhitelistResponse) Descriptor() ([]byte, []int) {
|
||||
return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{14}
|
||||
}
|
||||
|
||||
func (x *CheckLoginRiskIPWhitelistResponse) GetWhitelisted() bool {
|
||||
if x != nil {
|
||||
return x.Whitelisted
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AppHeartbeatRequest 刷新当前登录会话的 App 在线心跳。
|
||||
type AppHeartbeatRequest struct {
|
||||
state protoimpl.MessageState
|
||||
@ -1055,7 +1155,7 @@ type AppHeartbeatRequest struct {
|
||||
|
||||
func (x *AppHeartbeatRequest) Reset() {
|
||||
*x = AppHeartbeatRequest{}
|
||||
mi := &file_proto_user_v1_auth_proto_msgTypes[13]
|
||||
mi := &file_proto_user_v1_auth_proto_msgTypes[15]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@ -1067,7 +1167,7 @@ func (x *AppHeartbeatRequest) String() string {
|
||||
func (*AppHeartbeatRequest) ProtoMessage() {}
|
||||
|
||||
func (x *AppHeartbeatRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_user_v1_auth_proto_msgTypes[13]
|
||||
mi := &file_proto_user_v1_auth_proto_msgTypes[15]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@ -1080,7 +1180,7 @@ func (x *AppHeartbeatRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use AppHeartbeatRequest.ProtoReflect.Descriptor instead.
|
||||
func (*AppHeartbeatRequest) Descriptor() ([]byte, []int) {
|
||||
return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{13}
|
||||
return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{15}
|
||||
}
|
||||
|
||||
func (x *AppHeartbeatRequest) GetMeta() *RequestMeta {
|
||||
@ -1118,7 +1218,7 @@ type AppHeartbeatResponse struct {
|
||||
|
||||
func (x *AppHeartbeatResponse) Reset() {
|
||||
*x = AppHeartbeatResponse{}
|
||||
mi := &file_proto_user_v1_auth_proto_msgTypes[14]
|
||||
mi := &file_proto_user_v1_auth_proto_msgTypes[16]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@ -1130,7 +1230,7 @@ func (x *AppHeartbeatResponse) String() string {
|
||||
func (*AppHeartbeatResponse) ProtoMessage() {}
|
||||
|
||||
func (x *AppHeartbeatResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_proto_user_v1_auth_proto_msgTypes[14]
|
||||
mi := &file_proto_user_v1_auth_proto_msgTypes[16]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@ -1143,7 +1243,7 @@ func (x *AppHeartbeatResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use AppHeartbeatResponse.ProtoReflect.Descriptor instead.
|
||||
func (*AppHeartbeatResponse) Descriptor() ([]byte, []int) {
|
||||
return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{14}
|
||||
return file_proto_user_v1_auth_proto_rawDescGZIP(), []int{16}
|
||||
}
|
||||
|
||||
func (x *AppHeartbeatResponse) GetAccepted() bool {
|
||||
@ -1335,75 +1435,95 @@ var file_proto_user_v1_auth_proto_rawDesc = []byte{
|
||||
0x73, 0x6f, 0x6e, 0x22, 0x38, 0x0a, 0x1a, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c, 0x6f, 0x67,
|
||||
0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x65, 0x64, 0x18, 0x01, 0x20,
|
||||
0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x65, 0x64, 0x22, 0x7d, 0x0a,
|
||||
0x13, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01,
|
||||
0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e,
|
||||
0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x04,
|
||||
0x6d, 0x65, 0x74, 0x61, 0x12, 0x17, 0x0a, 0x07, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x75, 0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1d, 0x0a,
|
||||
0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28,
|
||||
0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x22, 0xb0, 0x01, 0x0a,
|
||||
0x14, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65,
|
||||
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65,
|
||||
0x64, 0x12, 0x26, 0x0a, 0x0f, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x5f, 0x61,
|
||||
0x74, 0x5f, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x68, 0x65, 0x61, 0x72,
|
||||
0x74, 0x62, 0x65, 0x61, 0x74, 0x41, 0x74, 0x4d, 0x73, 0x12, 0x24, 0x0a, 0x0e, 0x73, 0x65, 0x72,
|
||||
0x76, 0x65, 0x72, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28,
|
||||
0x03, 0x52, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x73, 0x12,
|
||||
0x2e, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18,
|
||||
0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x41,
|
||||
0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x32,
|
||||
0xdc, 0x05, 0x0a, 0x0b, 0x41, 0x75, 0x74, 0x68, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12,
|
||||
0x51, 0x0a, 0x0d, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64,
|
||||
0x12, 0x23, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31,
|
||||
0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65,
|
||||
0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73,
|
||||
0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x12, 0x55, 0x0a, 0x0f, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x54, 0x68, 0x69, 0x72, 0x64,
|
||||
0x50, 0x61, 0x72, 0x74, 0x79, 0x12, 0x25, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73,
|
||||
0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x54, 0x68, 0x69, 0x72, 0x64,
|
||||
0x50, 0x61, 0x72, 0x74, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x68,
|
||||
0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74,
|
||||
0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x54, 0x0a, 0x0b, 0x53, 0x65, 0x74,
|
||||
0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x21, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70,
|
||||
0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x50, 0x61, 0x73, 0x73,
|
||||
0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x79,
|
||||
0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x50,
|
||||
0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
|
||||
0x69, 0x0a, 0x12, 0x51, 0x75, 0x69, 0x63, 0x6b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x63,
|
||||
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73,
|
||||
0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x69, 0x63, 0x6b, 0x43, 0x72, 0x65, 0x61, 0x74,
|
||||
0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
|
||||
0x29, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e,
|
||||
0x51, 0x75, 0x69, 0x63, 0x6b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75,
|
||||
0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x0c, 0x52, 0x65,
|
||||
0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x22, 0x2e, 0x68, 0x79, 0x61,
|
||||
0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65,
|
||||
0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23,
|
||||
0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52,
|
||||
0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x06, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x12, 0x1c, 0x2e,
|
||||
0x01, 0x28, 0x08, 0x52, 0x08, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x65, 0x64, 0x22, 0x6f, 0x0a,
|
||||
0x20, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x69, 0x73, 0x6b, 0x49,
|
||||
0x50, 0x57, 0x68, 0x69, 0x74, 0x65, 0x6c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x12, 0x2e, 0x0a, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32,
|
||||
0x1a, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x04, 0x6d, 0x65, 0x74,
|
||||
0x61, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x70, 0x22, 0x45,
|
||||
0x0a, 0x21, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x69, 0x73, 0x6b,
|
||||
0x49, 0x50, 0x57, 0x68, 0x69, 0x74, 0x65, 0x6c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f,
|
||||
0x6e, 0x73, 0x65, 0x12, 0x20, 0x0a, 0x0b, 0x77, 0x68, 0x69, 0x74, 0x65, 0x6c, 0x69, 0x73, 0x74,
|
||||
0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x77, 0x68, 0x69, 0x74, 0x65, 0x6c,
|
||||
0x69, 0x73, 0x74, 0x65, 0x64, 0x22, 0x7d, 0x0a, 0x13, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x72,
|
||||
0x74, 0x62, 0x65, 0x61, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x2e, 0x0a, 0x04,
|
||||
0x6d, 0x65, 0x74, 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x68, 0x79, 0x61,
|
||||
0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||
0x73, 0x74, 0x4d, 0x65, 0x74, 0x61, 0x52, 0x04, 0x6d, 0x65, 0x74, 0x61, 0x12, 0x17, 0x0a, 0x07,
|
||||
0x75, 0x73, 0x65, 0x72, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x75,
|
||||
0x73, 0x65, 0x72, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e,
|
||||
0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69,
|
||||
0x6f, 0x6e, 0x49, 0x64, 0x22, 0xb0, 0x01, 0x0a, 0x14, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x72,
|
||||
0x74, 0x62, 0x65, 0x61, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1a, 0x0a,
|
||||
0x08, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52,
|
||||
0x08, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x65, 0x64, 0x12, 0x26, 0x0a, 0x0f, 0x68, 0x65, 0x61,
|
||||
0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x5f, 0x61, 0x74, 0x5f, 0x6d, 0x73, 0x18, 0x02, 0x20, 0x01,
|
||||
0x28, 0x03, 0x52, 0x0d, 0x68, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x41, 0x74, 0x4d,
|
||||
0x73, 0x12, 0x24, 0x0a, 0x0e, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x74, 0x69, 0x6d, 0x65,
|
||||
0x5f, 0x6d, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0c, 0x73, 0x65, 0x72, 0x76, 0x65,
|
||||
0x72, 0x54, 0x69, 0x6d, 0x65, 0x4d, 0x73, 0x12, 0x2e, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e,
|
||||
0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75,
|
||||
0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e,
|
||||
0x52, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x32, 0xdc, 0x06, 0x0a, 0x0b, 0x41, 0x75, 0x74, 0x68,
|
||||
0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x51, 0x0a, 0x0d, 0x4c, 0x6f, 0x67, 0x69, 0x6e,
|
||||
0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x23, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70,
|
||||
0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x50, 0x61,
|
||||
0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e,
|
||||
0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75,
|
||||
0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x55, 0x0a, 0x0f, 0x4c, 0x6f,
|
||||
0x67, 0x69, 0x6e, 0x54, 0x68, 0x69, 0x72, 0x64, 0x50, 0x61, 0x72, 0x74, 0x79, 0x12, 0x25, 0x2e,
|
||||
0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f,
|
||||
0x67, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x68, 0x79,
|
||||
0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x6f,
|
||||
0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x69, 0x0a, 0x12, 0x52, 0x65,
|
||||
0x63, 0x6f, 0x72, 0x64, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64,
|
||||
0x12, 0x28, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31,
|
||||
0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63,
|
||||
0x6b, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x68, 0x79, 0x61,
|
||||
0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72,
|
||||
0x64, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x52, 0x65, 0x73,
|
||||
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a, 0x0c, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x72,
|
||||
0x74, 0x62, 0x65, 0x61, 0x74, 0x12, 0x22, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73,
|
||||
0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65,
|
||||
0x61, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x68, 0x79, 0x61, 0x70,
|
||||
0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61,
|
||||
0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x26,
|
||||
0x5a, 0x24, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x2f, 0x61, 0x70,
|
||||
0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x75, 0x73, 0x65, 0x72, 0x2f, 0x76, 0x31, 0x3b,
|
||||
0x75, 0x73, 0x65, 0x72, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
0x67, 0x69, 0x6e, 0x54, 0x68, 0x69, 0x72, 0x64, 0x50, 0x61, 0x72, 0x74, 0x79, 0x52, 0x65, 0x71,
|
||||
0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65,
|
||||
0x72, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
|
||||
0x65, 0x12, 0x54, 0x0a, 0x0b, 0x53, 0x65, 0x74, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64,
|
||||
0x12, 0x21, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31,
|
||||
0x2e, 0x53, 0x65, 0x74, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72,
|
||||
0x2e, 0x76, 0x31, 0x2e, 0x53, 0x65, 0x74, 0x50, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x52,
|
||||
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x69, 0x0a, 0x12, 0x51, 0x75, 0x69, 0x63, 0x6b,
|
||||
0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x28, 0x2e,
|
||||
0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75,
|
||||
0x69, 0x63, 0x6b, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74,
|
||||
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e,
|
||||
0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x69, 0x63, 0x6b, 0x43, 0x72, 0x65,
|
||||
0x61, 0x74, 0x65, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x12, 0x57, 0x0a, 0x0c, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b,
|
||||
0x65, 0x6e, 0x12, 0x22, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e,
|
||||
0x76, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x52,
|
||||
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75,
|
||||
0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x54, 0x6f,
|
||||
0x6b, 0x65, 0x6e, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x45, 0x0a, 0x06, 0x4c,
|
||||
0x6f, 0x67, 0x6f, 0x75, 0x74, 0x12, 0x1c, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73,
|
||||
0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x71, 0x75,
|
||||
0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72,
|
||||
0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
|
||||
0x73, 0x65, 0x12, 0x69, 0x0a, 0x12, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c, 0x6f, 0x67, 0x69,
|
||||
0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x12, 0x28, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70,
|
||||
0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c,
|
||||
0x6f, 0x67, 0x69, 0x6e, 0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x65, 0x64, 0x52, 0x65, 0x71, 0x75, 0x65,
|
||||
0x73, 0x74, 0x1a, 0x29, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e,
|
||||
0x76, 0x31, 0x2e, 0x52, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x42, 0x6c,
|
||||
0x6f, 0x63, 0x6b, 0x65, 0x64, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x7e, 0x0a,
|
||||
0x19, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x69, 0x73, 0x6b, 0x49,
|
||||
0x50, 0x57, 0x68, 0x69, 0x74, 0x65, 0x6c, 0x69, 0x73, 0x74, 0x12, 0x2f, 0x2e, 0x68, 0x79, 0x61,
|
||||
0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63, 0x6b,
|
||||
0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x69, 0x73, 0x6b, 0x49, 0x50, 0x57, 0x68, 0x69, 0x74, 0x65,
|
||||
0x6c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x30, 0x2e, 0x68, 0x79,
|
||||
0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x68, 0x65, 0x63,
|
||||
0x6b, 0x4c, 0x6f, 0x67, 0x69, 0x6e, 0x52, 0x69, 0x73, 0x6b, 0x49, 0x50, 0x57, 0x68, 0x69, 0x74,
|
||||
0x65, 0x6c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x57, 0x0a,
|
||||
0x0c, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x12, 0x22, 0x2e,
|
||||
0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x70,
|
||||
0x70, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
|
||||
0x74, 0x1a, 0x23, 0x2e, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e, 0x75, 0x73, 0x65, 0x72, 0x2e, 0x76,
|
||||
0x31, 0x2e, 0x41, 0x70, 0x70, 0x48, 0x65, 0x61, 0x72, 0x74, 0x62, 0x65, 0x61, 0x74, 0x52, 0x65,
|
||||
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x26, 0x5a, 0x24, 0x68, 0x79, 0x61, 0x70, 0x70, 0x2e,
|
||||
0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f,
|
||||
0x75, 0x73, 0x65, 0x72, 0x2f, 0x76, 0x31, 0x3b, 0x75, 0x73, 0x65, 0x72, 0x76, 0x31, 0x62, 0x06,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
@ -1418,60 +1538,65 @@ func file_proto_user_v1_auth_proto_rawDescGZIP() []byte {
|
||||
return file_proto_user_v1_auth_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_proto_user_v1_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 15)
|
||||
var file_proto_user_v1_auth_proto_msgTypes = make([]protoimpl.MessageInfo, 17)
|
||||
var file_proto_user_v1_auth_proto_goTypes = []any{
|
||||
(*LoginPasswordRequest)(nil), // 0: hyapp.user.v1.LoginPasswordRequest
|
||||
(*LoginThirdPartyRequest)(nil), // 1: hyapp.user.v1.LoginThirdPartyRequest
|
||||
(*AuthResponse)(nil), // 2: hyapp.user.v1.AuthResponse
|
||||
(*SetPasswordRequest)(nil), // 3: hyapp.user.v1.SetPasswordRequest
|
||||
(*SetPasswordResponse)(nil), // 4: hyapp.user.v1.SetPasswordResponse
|
||||
(*QuickCreateAccountRequest)(nil), // 5: hyapp.user.v1.QuickCreateAccountRequest
|
||||
(*QuickCreateAccountResponse)(nil), // 6: hyapp.user.v1.QuickCreateAccountResponse
|
||||
(*RefreshTokenRequest)(nil), // 7: hyapp.user.v1.RefreshTokenRequest
|
||||
(*RefreshTokenResponse)(nil), // 8: hyapp.user.v1.RefreshTokenResponse
|
||||
(*LogoutRequest)(nil), // 9: hyapp.user.v1.LogoutRequest
|
||||
(*LogoutResponse)(nil), // 10: hyapp.user.v1.LogoutResponse
|
||||
(*RecordLoginBlockedRequest)(nil), // 11: hyapp.user.v1.RecordLoginBlockedRequest
|
||||
(*RecordLoginBlockedResponse)(nil), // 12: hyapp.user.v1.RecordLoginBlockedResponse
|
||||
(*AppHeartbeatRequest)(nil), // 13: hyapp.user.v1.AppHeartbeatRequest
|
||||
(*AppHeartbeatResponse)(nil), // 14: hyapp.user.v1.AppHeartbeatResponse
|
||||
(*RequestMeta)(nil), // 15: hyapp.user.v1.RequestMeta
|
||||
(*AuthToken)(nil), // 16: hyapp.user.v1.AuthToken
|
||||
(*LoginPasswordRequest)(nil), // 0: hyapp.user.v1.LoginPasswordRequest
|
||||
(*LoginThirdPartyRequest)(nil), // 1: hyapp.user.v1.LoginThirdPartyRequest
|
||||
(*AuthResponse)(nil), // 2: hyapp.user.v1.AuthResponse
|
||||
(*SetPasswordRequest)(nil), // 3: hyapp.user.v1.SetPasswordRequest
|
||||
(*SetPasswordResponse)(nil), // 4: hyapp.user.v1.SetPasswordResponse
|
||||
(*QuickCreateAccountRequest)(nil), // 5: hyapp.user.v1.QuickCreateAccountRequest
|
||||
(*QuickCreateAccountResponse)(nil), // 6: hyapp.user.v1.QuickCreateAccountResponse
|
||||
(*RefreshTokenRequest)(nil), // 7: hyapp.user.v1.RefreshTokenRequest
|
||||
(*RefreshTokenResponse)(nil), // 8: hyapp.user.v1.RefreshTokenResponse
|
||||
(*LogoutRequest)(nil), // 9: hyapp.user.v1.LogoutRequest
|
||||
(*LogoutResponse)(nil), // 10: hyapp.user.v1.LogoutResponse
|
||||
(*RecordLoginBlockedRequest)(nil), // 11: hyapp.user.v1.RecordLoginBlockedRequest
|
||||
(*RecordLoginBlockedResponse)(nil), // 12: hyapp.user.v1.RecordLoginBlockedResponse
|
||||
(*CheckLoginRiskIPWhitelistRequest)(nil), // 13: hyapp.user.v1.CheckLoginRiskIPWhitelistRequest
|
||||
(*CheckLoginRiskIPWhitelistResponse)(nil), // 14: hyapp.user.v1.CheckLoginRiskIPWhitelistResponse
|
||||
(*AppHeartbeatRequest)(nil), // 15: hyapp.user.v1.AppHeartbeatRequest
|
||||
(*AppHeartbeatResponse)(nil), // 16: hyapp.user.v1.AppHeartbeatResponse
|
||||
(*RequestMeta)(nil), // 17: hyapp.user.v1.RequestMeta
|
||||
(*AuthToken)(nil), // 18: hyapp.user.v1.AuthToken
|
||||
}
|
||||
var file_proto_user_v1_auth_proto_depIdxs = []int32{
|
||||
15, // 0: hyapp.user.v1.LoginPasswordRequest.meta:type_name -> hyapp.user.v1.RequestMeta
|
||||
15, // 1: hyapp.user.v1.LoginThirdPartyRequest.meta:type_name -> hyapp.user.v1.RequestMeta
|
||||
16, // 2: hyapp.user.v1.AuthResponse.token:type_name -> hyapp.user.v1.AuthToken
|
||||
15, // 3: hyapp.user.v1.SetPasswordRequest.meta:type_name -> hyapp.user.v1.RequestMeta
|
||||
15, // 4: hyapp.user.v1.QuickCreateAccountRequest.meta:type_name -> hyapp.user.v1.RequestMeta
|
||||
16, // 5: hyapp.user.v1.QuickCreateAccountResponse.token:type_name -> hyapp.user.v1.AuthToken
|
||||
15, // 6: hyapp.user.v1.RefreshTokenRequest.meta:type_name -> hyapp.user.v1.RequestMeta
|
||||
16, // 7: hyapp.user.v1.RefreshTokenResponse.token:type_name -> hyapp.user.v1.AuthToken
|
||||
15, // 8: hyapp.user.v1.LogoutRequest.meta:type_name -> hyapp.user.v1.RequestMeta
|
||||
15, // 9: hyapp.user.v1.RecordLoginBlockedRequest.meta:type_name -> hyapp.user.v1.RequestMeta
|
||||
15, // 10: hyapp.user.v1.AppHeartbeatRequest.meta:type_name -> hyapp.user.v1.RequestMeta
|
||||
16, // 11: hyapp.user.v1.AppHeartbeatResponse.token:type_name -> hyapp.user.v1.AuthToken
|
||||
0, // 12: hyapp.user.v1.AuthService.LoginPassword:input_type -> hyapp.user.v1.LoginPasswordRequest
|
||||
1, // 13: hyapp.user.v1.AuthService.LoginThirdParty:input_type -> hyapp.user.v1.LoginThirdPartyRequest
|
||||
3, // 14: hyapp.user.v1.AuthService.SetPassword:input_type -> hyapp.user.v1.SetPasswordRequest
|
||||
5, // 15: hyapp.user.v1.AuthService.QuickCreateAccount:input_type -> hyapp.user.v1.QuickCreateAccountRequest
|
||||
7, // 16: hyapp.user.v1.AuthService.RefreshToken:input_type -> hyapp.user.v1.RefreshTokenRequest
|
||||
9, // 17: hyapp.user.v1.AuthService.Logout:input_type -> hyapp.user.v1.LogoutRequest
|
||||
11, // 18: hyapp.user.v1.AuthService.RecordLoginBlocked:input_type -> hyapp.user.v1.RecordLoginBlockedRequest
|
||||
13, // 19: hyapp.user.v1.AuthService.AppHeartbeat:input_type -> hyapp.user.v1.AppHeartbeatRequest
|
||||
2, // 20: hyapp.user.v1.AuthService.LoginPassword:output_type -> hyapp.user.v1.AuthResponse
|
||||
2, // 21: hyapp.user.v1.AuthService.LoginThirdParty:output_type -> hyapp.user.v1.AuthResponse
|
||||
4, // 22: hyapp.user.v1.AuthService.SetPassword:output_type -> hyapp.user.v1.SetPasswordResponse
|
||||
6, // 23: hyapp.user.v1.AuthService.QuickCreateAccount:output_type -> hyapp.user.v1.QuickCreateAccountResponse
|
||||
8, // 24: hyapp.user.v1.AuthService.RefreshToken:output_type -> hyapp.user.v1.RefreshTokenResponse
|
||||
10, // 25: hyapp.user.v1.AuthService.Logout:output_type -> hyapp.user.v1.LogoutResponse
|
||||
12, // 26: hyapp.user.v1.AuthService.RecordLoginBlocked:output_type -> hyapp.user.v1.RecordLoginBlockedResponse
|
||||
14, // 27: hyapp.user.v1.AuthService.AppHeartbeat:output_type -> hyapp.user.v1.AppHeartbeatResponse
|
||||
20, // [20:28] is the sub-list for method output_type
|
||||
12, // [12:20] is the sub-list for method input_type
|
||||
12, // [12:12] is the sub-list for extension type_name
|
||||
12, // [12:12] is the sub-list for extension extendee
|
||||
0, // [0:12] is the sub-list for field type_name
|
||||
17, // 0: hyapp.user.v1.LoginPasswordRequest.meta:type_name -> hyapp.user.v1.RequestMeta
|
||||
17, // 1: hyapp.user.v1.LoginThirdPartyRequest.meta:type_name -> hyapp.user.v1.RequestMeta
|
||||
18, // 2: hyapp.user.v1.AuthResponse.token:type_name -> hyapp.user.v1.AuthToken
|
||||
17, // 3: hyapp.user.v1.SetPasswordRequest.meta:type_name -> hyapp.user.v1.RequestMeta
|
||||
17, // 4: hyapp.user.v1.QuickCreateAccountRequest.meta:type_name -> hyapp.user.v1.RequestMeta
|
||||
18, // 5: hyapp.user.v1.QuickCreateAccountResponse.token:type_name -> hyapp.user.v1.AuthToken
|
||||
17, // 6: hyapp.user.v1.RefreshTokenRequest.meta:type_name -> hyapp.user.v1.RequestMeta
|
||||
18, // 7: hyapp.user.v1.RefreshTokenResponse.token:type_name -> hyapp.user.v1.AuthToken
|
||||
17, // 8: hyapp.user.v1.LogoutRequest.meta:type_name -> hyapp.user.v1.RequestMeta
|
||||
17, // 9: hyapp.user.v1.RecordLoginBlockedRequest.meta:type_name -> hyapp.user.v1.RequestMeta
|
||||
17, // 10: hyapp.user.v1.CheckLoginRiskIPWhitelistRequest.meta:type_name -> hyapp.user.v1.RequestMeta
|
||||
17, // 11: hyapp.user.v1.AppHeartbeatRequest.meta:type_name -> hyapp.user.v1.RequestMeta
|
||||
18, // 12: hyapp.user.v1.AppHeartbeatResponse.token:type_name -> hyapp.user.v1.AuthToken
|
||||
0, // 13: hyapp.user.v1.AuthService.LoginPassword:input_type -> hyapp.user.v1.LoginPasswordRequest
|
||||
1, // 14: hyapp.user.v1.AuthService.LoginThirdParty:input_type -> hyapp.user.v1.LoginThirdPartyRequest
|
||||
3, // 15: hyapp.user.v1.AuthService.SetPassword:input_type -> hyapp.user.v1.SetPasswordRequest
|
||||
5, // 16: hyapp.user.v1.AuthService.QuickCreateAccount:input_type -> hyapp.user.v1.QuickCreateAccountRequest
|
||||
7, // 17: hyapp.user.v1.AuthService.RefreshToken:input_type -> hyapp.user.v1.RefreshTokenRequest
|
||||
9, // 18: hyapp.user.v1.AuthService.Logout:input_type -> hyapp.user.v1.LogoutRequest
|
||||
11, // 19: hyapp.user.v1.AuthService.RecordLoginBlocked:input_type -> hyapp.user.v1.RecordLoginBlockedRequest
|
||||
13, // 20: hyapp.user.v1.AuthService.CheckLoginRiskIPWhitelist:input_type -> hyapp.user.v1.CheckLoginRiskIPWhitelistRequest
|
||||
15, // 21: hyapp.user.v1.AuthService.AppHeartbeat:input_type -> hyapp.user.v1.AppHeartbeatRequest
|
||||
2, // 22: hyapp.user.v1.AuthService.LoginPassword:output_type -> hyapp.user.v1.AuthResponse
|
||||
2, // 23: hyapp.user.v1.AuthService.LoginThirdParty:output_type -> hyapp.user.v1.AuthResponse
|
||||
4, // 24: hyapp.user.v1.AuthService.SetPassword:output_type -> hyapp.user.v1.SetPasswordResponse
|
||||
6, // 25: hyapp.user.v1.AuthService.QuickCreateAccount:output_type -> hyapp.user.v1.QuickCreateAccountResponse
|
||||
8, // 26: hyapp.user.v1.AuthService.RefreshToken:output_type -> hyapp.user.v1.RefreshTokenResponse
|
||||
10, // 27: hyapp.user.v1.AuthService.Logout:output_type -> hyapp.user.v1.LogoutResponse
|
||||
12, // 28: hyapp.user.v1.AuthService.RecordLoginBlocked:output_type -> hyapp.user.v1.RecordLoginBlockedResponse
|
||||
14, // 29: hyapp.user.v1.AuthService.CheckLoginRiskIPWhitelist:output_type -> hyapp.user.v1.CheckLoginRiskIPWhitelistResponse
|
||||
16, // 30: hyapp.user.v1.AuthService.AppHeartbeat:output_type -> hyapp.user.v1.AppHeartbeatResponse
|
||||
22, // [22:31] is the sub-list for method output_type
|
||||
13, // [13:22] is the sub-list for method input_type
|
||||
13, // [13:13] is the sub-list for extension type_name
|
||||
13, // [13:13] is the sub-list for extension extendee
|
||||
0, // [0:13] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_proto_user_v1_auth_proto_init() }
|
||||
@ -1486,7 +1611,7 @@ func file_proto_user_v1_auth_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_proto_user_v1_auth_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 15,
|
||||
NumMessages: 17,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
|
||||
@ -124,6 +124,17 @@ message RecordLoginBlockedResponse {
|
||||
bool recorded = 1;
|
||||
}
|
||||
|
||||
// CheckLoginRiskIPWhitelistRequest 查询入口 IP 是否命中后台白名单。
|
||||
message CheckLoginRiskIPWhitelistRequest {
|
||||
RequestMeta meta = 1;
|
||||
string client_ip = 2;
|
||||
}
|
||||
|
||||
// CheckLoginRiskIPWhitelistResponse 返回入口 IP 是否跳过登录地区屏蔽。
|
||||
message CheckLoginRiskIPWhitelistResponse {
|
||||
bool whitelisted = 1;
|
||||
}
|
||||
|
||||
// AppHeartbeatRequest 刷新当前登录会话的 App 在线心跳。
|
||||
message AppHeartbeatRequest {
|
||||
RequestMeta meta = 1;
|
||||
@ -148,5 +159,6 @@ service AuthService {
|
||||
rpc RefreshToken(RefreshTokenRequest) returns (RefreshTokenResponse);
|
||||
rpc Logout(LogoutRequest) returns (LogoutResponse);
|
||||
rpc RecordLoginBlocked(RecordLoginBlockedRequest) returns (RecordLoginBlockedResponse);
|
||||
rpc CheckLoginRiskIPWhitelist(CheckLoginRiskIPWhitelistRequest) returns (CheckLoginRiskIPWhitelistResponse);
|
||||
rpc AppHeartbeat(AppHeartbeatRequest) returns (AppHeartbeatResponse);
|
||||
}
|
||||
|
||||
@ -19,14 +19,15 @@ import (
|
||||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
AuthService_LoginPassword_FullMethodName = "/hyapp.user.v1.AuthService/LoginPassword"
|
||||
AuthService_LoginThirdParty_FullMethodName = "/hyapp.user.v1.AuthService/LoginThirdParty"
|
||||
AuthService_SetPassword_FullMethodName = "/hyapp.user.v1.AuthService/SetPassword"
|
||||
AuthService_QuickCreateAccount_FullMethodName = "/hyapp.user.v1.AuthService/QuickCreateAccount"
|
||||
AuthService_RefreshToken_FullMethodName = "/hyapp.user.v1.AuthService/RefreshToken"
|
||||
AuthService_Logout_FullMethodName = "/hyapp.user.v1.AuthService/Logout"
|
||||
AuthService_RecordLoginBlocked_FullMethodName = "/hyapp.user.v1.AuthService/RecordLoginBlocked"
|
||||
AuthService_AppHeartbeat_FullMethodName = "/hyapp.user.v1.AuthService/AppHeartbeat"
|
||||
AuthService_LoginPassword_FullMethodName = "/hyapp.user.v1.AuthService/LoginPassword"
|
||||
AuthService_LoginThirdParty_FullMethodName = "/hyapp.user.v1.AuthService/LoginThirdParty"
|
||||
AuthService_SetPassword_FullMethodName = "/hyapp.user.v1.AuthService/SetPassword"
|
||||
AuthService_QuickCreateAccount_FullMethodName = "/hyapp.user.v1.AuthService/QuickCreateAccount"
|
||||
AuthService_RefreshToken_FullMethodName = "/hyapp.user.v1.AuthService/RefreshToken"
|
||||
AuthService_Logout_FullMethodName = "/hyapp.user.v1.AuthService/Logout"
|
||||
AuthService_RecordLoginBlocked_FullMethodName = "/hyapp.user.v1.AuthService/RecordLoginBlocked"
|
||||
AuthService_CheckLoginRiskIPWhitelist_FullMethodName = "/hyapp.user.v1.AuthService/CheckLoginRiskIPWhitelist"
|
||||
AuthService_AppHeartbeat_FullMethodName = "/hyapp.user.v1.AuthService/AppHeartbeat"
|
||||
)
|
||||
|
||||
// AuthServiceClient is the client API for AuthService service.
|
||||
@ -42,6 +43,7 @@ type AuthServiceClient interface {
|
||||
RefreshToken(ctx context.Context, in *RefreshTokenRequest, opts ...grpc.CallOption) (*RefreshTokenResponse, error)
|
||||
Logout(ctx context.Context, in *LogoutRequest, opts ...grpc.CallOption) (*LogoutResponse, error)
|
||||
RecordLoginBlocked(ctx context.Context, in *RecordLoginBlockedRequest, opts ...grpc.CallOption) (*RecordLoginBlockedResponse, error)
|
||||
CheckLoginRiskIPWhitelist(ctx context.Context, in *CheckLoginRiskIPWhitelistRequest, opts ...grpc.CallOption) (*CheckLoginRiskIPWhitelistResponse, error)
|
||||
AppHeartbeat(ctx context.Context, in *AppHeartbeatRequest, opts ...grpc.CallOption) (*AppHeartbeatResponse, error)
|
||||
}
|
||||
|
||||
@ -123,6 +125,16 @@ func (c *authServiceClient) RecordLoginBlocked(ctx context.Context, in *RecordLo
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *authServiceClient) CheckLoginRiskIPWhitelist(ctx context.Context, in *CheckLoginRiskIPWhitelistRequest, opts ...grpc.CallOption) (*CheckLoginRiskIPWhitelistResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(CheckLoginRiskIPWhitelistResponse)
|
||||
err := c.cc.Invoke(ctx, AuthService_CheckLoginRiskIPWhitelist_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *authServiceClient) AppHeartbeat(ctx context.Context, in *AppHeartbeatRequest, opts ...grpc.CallOption) (*AppHeartbeatResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(AppHeartbeatResponse)
|
||||
@ -146,6 +158,7 @@ type AuthServiceServer interface {
|
||||
RefreshToken(context.Context, *RefreshTokenRequest) (*RefreshTokenResponse, error)
|
||||
Logout(context.Context, *LogoutRequest) (*LogoutResponse, error)
|
||||
RecordLoginBlocked(context.Context, *RecordLoginBlockedRequest) (*RecordLoginBlockedResponse, error)
|
||||
CheckLoginRiskIPWhitelist(context.Context, *CheckLoginRiskIPWhitelistRequest) (*CheckLoginRiskIPWhitelistResponse, error)
|
||||
AppHeartbeat(context.Context, *AppHeartbeatRequest) (*AppHeartbeatResponse, error)
|
||||
mustEmbedUnimplementedAuthServiceServer()
|
||||
}
|
||||
@ -178,6 +191,9 @@ func (UnimplementedAuthServiceServer) Logout(context.Context, *LogoutRequest) (*
|
||||
func (UnimplementedAuthServiceServer) RecordLoginBlocked(context.Context, *RecordLoginBlockedRequest) (*RecordLoginBlockedResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method RecordLoginBlocked not implemented")
|
||||
}
|
||||
func (UnimplementedAuthServiceServer) CheckLoginRiskIPWhitelist(context.Context, *CheckLoginRiskIPWhitelistRequest) (*CheckLoginRiskIPWhitelistResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CheckLoginRiskIPWhitelist not implemented")
|
||||
}
|
||||
func (UnimplementedAuthServiceServer) AppHeartbeat(context.Context, *AppHeartbeatRequest) (*AppHeartbeatResponse, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method AppHeartbeat not implemented")
|
||||
}
|
||||
@ -328,6 +344,24 @@ func _AuthService_RecordLoginBlocked_Handler(srv interface{}, ctx context.Contex
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AuthService_CheckLoginRiskIPWhitelist_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CheckLoginRiskIPWhitelistRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(AuthServiceServer).CheckLoginRiskIPWhitelist(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: AuthService_CheckLoginRiskIPWhitelist_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(AuthServiceServer).CheckLoginRiskIPWhitelist(ctx, req.(*CheckLoginRiskIPWhitelistRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _AuthService_AppHeartbeat_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(AppHeartbeatRequest)
|
||||
if err := dec(in); err != nil {
|
||||
@ -381,6 +415,10 @@ var AuthService_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "RecordLoginBlocked",
|
||||
Handler: _AuthService_RecordLoginBlocked_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "CheckLoginRiskIPWhitelist",
|
||||
Handler: _AuthService_CheckLoginRiskIPWhitelist_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "AppHeartbeat",
|
||||
Handler: _AuthService_AppHeartbeat_Handler,
|
||||
|
||||
14
scripts/mysql/050_login_risk_ip_whitelist.sql
Normal file
14
scripts/mysql/050_login_risk_ip_whitelist.sql
Normal file
@ -0,0 +1,14 @@
|
||||
CREATE TABLE IF NOT EXISTS login_risk_ip_whitelist (
|
||||
app_code VARCHAR(32) NOT NULL COMMENT '应用编码,用于多租户隔离',
|
||||
whitelist_id BIGINT NOT NULL AUTO_INCREMENT COMMENT '白名单 ID',
|
||||
ip_address VARCHAR(64) NOT NULL COMMENT '允许跳过登录地区屏蔽的客户端 IP',
|
||||
enabled TINYINT(1) NOT NULL DEFAULT 1 COMMENT '启用',
|
||||
created_by_user_id BIGINT NULL COMMENT '创建人用户 ID',
|
||||
updated_by_user_id BIGINT NULL COMMENT '更新人用户 ID',
|
||||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||||
PRIMARY KEY (whitelist_id),
|
||||
UNIQUE KEY uk_login_risk_ip_whitelist_ip (app_code, ip_address),
|
||||
KEY idx_login_risk_ip_whitelist_enabled (app_code, enabled, ip_address),
|
||||
KEY idx_login_risk_ip_whitelist_updated (app_code, updated_at_ms)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='登录风险 IP 白名单表';
|
||||
@ -18,7 +18,8 @@ type Handler struct {
|
||||
}
|
||||
|
||||
type replaceRequest struct {
|
||||
Keywords *[]string `json:"keywords"`
|
||||
Keywords *[]string `json:"keywords"`
|
||||
WhitelistIPs *[]string `json:"whitelist_ips"`
|
||||
}
|
||||
|
||||
func New(userDB *sql.DB, audit shared.OperationLogger) *Handler {
|
||||
@ -32,7 +33,7 @@ func (h *Handler) ListRegionBlocks(c *gin.Context) {
|
||||
response.ServerError(c, "获取地区屏蔽配置失败")
|
||||
return
|
||||
}
|
||||
response.OK(c, gin.H{"items": items, "total": len(items)})
|
||||
response.OK(c, items)
|
||||
}
|
||||
|
||||
// ReplaceRegionBlocks 保存整页地区屏蔽词配置;空数组表示清空屏蔽词。
|
||||
@ -42,7 +43,7 @@ func (h *Handler) ReplaceRegionBlocks(c *gin.Context) {
|
||||
response.BadRequest(c, "地区屏蔽词参数不正确")
|
||||
return
|
||||
}
|
||||
items, err := h.service.Replace(c.Request.Context(), int64(shared.ActorFromContext(c).UserID), *req.Keywords)
|
||||
items, err := h.service.Replace(c.Request.Context(), int64(shared.ActorFromContext(c).UserID), *req.Keywords, req.WhitelistIPs)
|
||||
if err != nil {
|
||||
if errors.Is(err, errInvalidPayload) {
|
||||
response.BadRequest(c, "地区屏蔽词参数不正确")
|
||||
@ -51,6 +52,6 @@ func (h *Handler) ReplaceRegionBlocks(c *gin.Context) {
|
||||
response.ServerError(c, "保存地区屏蔽配置失败")
|
||||
return
|
||||
}
|
||||
shared.OperationLogWithResourceID(c, h.audit, "replace-region-blocks", "login_risk_country_blocks", middleware.CurrentRequestID(c), "success", fmt.Sprintf("keywords=%d", len(items)))
|
||||
response.OK(c, gin.H{"items": items, "total": len(items)})
|
||||
shared.OperationLogWithResourceID(c, h.audit, "replace-region-blocks", "login_risk_country_blocks", middleware.CurrentRequestID(c), "success", fmt.Sprintf("keywords=%d whitelist_ips=%d", len(items.Items), len(items.WhitelistItems)))
|
||||
response.OK(c, items)
|
||||
}
|
||||
|
||||
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"net/netip"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
@ -15,6 +16,7 @@ import (
|
||||
const (
|
||||
maxRegionBlockKeywords = 200
|
||||
maxRegionBlockKeywordLength = 128
|
||||
maxIPWhitelistItems = 500
|
||||
)
|
||||
|
||||
var (
|
||||
@ -35,6 +37,21 @@ type RegionBlock struct {
|
||||
UpdatedAtMS int64 `json:"updated_at_ms"`
|
||||
}
|
||||
|
||||
type IPWhitelistItem struct {
|
||||
WhitelistID int64 `json:"whitelist_id"`
|
||||
IPAddress string `json:"ip_address"`
|
||||
Enabled bool `json:"enabled"`
|
||||
CreatedAtMS int64 `json:"created_at_ms"`
|
||||
UpdatedAtMS int64 `json:"updated_at_ms"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Items []RegionBlock `json:"items"`
|
||||
Total int `json:"total"`
|
||||
WhitelistItems []IPWhitelistItem `json:"whitelist_items"`
|
||||
WhitelistTotal int `json:"whitelist_total"`
|
||||
}
|
||||
|
||||
type normalizedKeyword struct {
|
||||
Keyword string
|
||||
CountryCode string
|
||||
@ -48,28 +65,44 @@ func NewService(userDB *sql.DB) *Service {
|
||||
return &Service{userDB: userDB}
|
||||
}
|
||||
|
||||
// List 返回当前 App 已启用的登录地区屏蔽词。
|
||||
// List 返回当前 App 已启用的登录地区屏蔽词和 IP 白名单。
|
||||
// 表属于 user-service 登录风控事实,admin-server 只作为后台维护入口直接写 user DB。
|
||||
func (s *Service) List(ctx context.Context) ([]RegionBlock, error) {
|
||||
func (s *Service) List(ctx context.Context) (Config, error) {
|
||||
if s.userDB == nil {
|
||||
return nil, errors.New("user db is not configured")
|
||||
return Config{}, errors.New("user db is not configured")
|
||||
}
|
||||
return queryEnabledBlocks(ctx, s.userDB, appctx.FromContext(ctx))
|
||||
appCode := appctx.FromContext(ctx)
|
||||
blocks, err := queryEnabledBlocks(ctx, s.userDB, appCode)
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
whitelist, err := queryEnabledIPWhitelist(ctx, s.userDB, appCode)
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
return Config{Items: blocks, Total: len(blocks), WhitelistItems: whitelist, WhitelistTotal: len(whitelist)}, nil
|
||||
}
|
||||
|
||||
// Replace 用页面提交的词表整体替换当前 App 的生效配置。
|
||||
// 整体替换比逐条增删更适合配置页保存语义,也能避免客户端和服务端出现半旧半新的屏蔽词集合。
|
||||
func (s *Service) Replace(ctx context.Context, actorID int64, keywords []string) ([]RegionBlock, error) {
|
||||
func (s *Service) Replace(ctx context.Context, actorID int64, keywords []string, whitelistIPs *[]string) (Config, error) {
|
||||
if s.userDB == nil {
|
||||
return nil, errors.New("user db is not configured")
|
||||
return Config{}, errors.New("user db is not configured")
|
||||
}
|
||||
normalized, err := normalizeKeywords(ctx, s.userDB, appctx.FromContext(ctx), keywords)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return Config{}, err
|
||||
}
|
||||
var normalizedIPs []string
|
||||
if whitelistIPs != nil {
|
||||
normalizedIPs, err = normalizeIPWhitelist(*whitelistIPs)
|
||||
if err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
}
|
||||
tx, err := s.userDB.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return Config{}, err
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
@ -80,7 +113,7 @@ func (s *Service) Replace(ctx context.Context, actorID int64, keywords []string)
|
||||
SET enabled = 0, updated_by_user_id = ?, updated_at_ms = ?
|
||||
WHERE app_code = ? AND enabled = 1
|
||||
`, nullableActor(actorID), nowMS, appCode); err != nil {
|
||||
return nil, err
|
||||
return Config{}, err
|
||||
}
|
||||
for _, keyword := range normalized {
|
||||
if _, err := tx.ExecContext(ctx, `
|
||||
@ -95,11 +128,35 @@ func (s *Service) Replace(ctx context.Context, actorID int64, keywords []string)
|
||||
updated_by_user_id = VALUES(updated_by_user_id),
|
||||
updated_at_ms = VALUES(updated_at_ms)
|
||||
`, appCode, keyword.Keyword, keyword.CountryCode, nullableActor(actorID), nullableActor(actorID), nowMS, nowMS); err != nil {
|
||||
return nil, err
|
||||
return Config{}, err
|
||||
}
|
||||
}
|
||||
if whitelistIPs != nil {
|
||||
if _, err := tx.ExecContext(ctx, `
|
||||
UPDATE login_risk_ip_whitelist
|
||||
SET enabled = 0, updated_by_user_id = ?, updated_at_ms = ?
|
||||
WHERE app_code = ? AND enabled = 1
|
||||
`, nullableActor(actorID), nowMS, appCode); err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
for _, ip := range normalizedIPs {
|
||||
if _, err := tx.ExecContext(ctx, `
|
||||
INSERT INTO login_risk_ip_whitelist (
|
||||
app_code, ip_address, enabled,
|
||||
created_by_user_id, updated_by_user_id, created_at_ms, updated_at_ms
|
||||
)
|
||||
VALUES (?, ?, 1, ?, ?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
enabled = 1,
|
||||
updated_by_user_id = VALUES(updated_by_user_id),
|
||||
updated_at_ms = VALUES(updated_at_ms)
|
||||
`, appCode, ip, nullableActor(actorID), nullableActor(actorID), nowMS, nowMS); err != nil {
|
||||
return Config{}, err
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := tx.Commit(); err != nil {
|
||||
return nil, err
|
||||
return Config{}, err
|
||||
}
|
||||
return s.List(ctx)
|
||||
}
|
||||
@ -132,6 +189,27 @@ func normalizeKeywords(ctx context.Context, querier countryCodeQuerier, appCode
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func normalizeIPWhitelist(raw []string) ([]string, error) {
|
||||
if len(raw) > maxIPWhitelistItems {
|
||||
return nil, errInvalidPayload
|
||||
}
|
||||
result := make([]string, 0, len(raw))
|
||||
seen := map[string]struct{}{}
|
||||
for _, item := range raw {
|
||||
addr, err := netip.ParseAddr(strings.TrimSpace(item))
|
||||
if err != nil {
|
||||
return nil, errInvalidPayload
|
||||
}
|
||||
ip := addr.String()
|
||||
if _, ok := seen[ip]; ok {
|
||||
continue
|
||||
}
|
||||
seen[ip] = struct{}{}
|
||||
result = append(result, ip)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func resolveCountryCode(ctx context.Context, querier countryCodeQuerier, appCode string, keyword string) (string, error) {
|
||||
if countryCodePattern.MatchString(keyword) {
|
||||
return strings.ToUpper(keyword), nil
|
||||
@ -186,6 +264,32 @@ func queryEnabledBlocks(ctx context.Context, db *sql.DB, appCode string) ([]Regi
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func queryEnabledIPWhitelist(ctx context.Context, db *sql.DB, appCode string) ([]IPWhitelistItem, error) {
|
||||
rows, err := db.QueryContext(ctx, `
|
||||
SELECT whitelist_id, ip_address, enabled, created_at_ms, updated_at_ms
|
||||
FROM login_risk_ip_whitelist
|
||||
WHERE app_code = ? AND enabled = 1
|
||||
ORDER BY ip_address ASC, whitelist_id ASC
|
||||
`, appCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
items := make([]IPWhitelistItem, 0)
|
||||
for rows.Next() {
|
||||
var item IPWhitelistItem
|
||||
if err := rows.Scan(&item.WhitelistID, &item.IPAddress, &item.Enabled, &item.CreatedAtMS, &item.UpdatedAtMS); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
func nullableActor(actorID int64) any {
|
||||
if actorID <= 0 {
|
||||
return nil
|
||||
|
||||
@ -17,6 +17,7 @@ type UserAuthClient interface {
|
||||
RefreshToken(ctx context.Context, req *userv1.RefreshTokenRequest) (*userv1.RefreshTokenResponse, error)
|
||||
Logout(ctx context.Context, req *userv1.LogoutRequest) (*userv1.LogoutResponse, error)
|
||||
RecordLoginBlocked(ctx context.Context, req *userv1.RecordLoginBlockedRequest) (*userv1.RecordLoginBlockedResponse, error)
|
||||
CheckLoginRiskIPWhitelist(ctx context.Context, req *userv1.CheckLoginRiskIPWhitelistRequest) (*userv1.CheckLoginRiskIPWhitelistResponse, error)
|
||||
AppHeartbeat(ctx context.Context, req *userv1.AppHeartbeatRequest) (*userv1.AppHeartbeatResponse, error)
|
||||
}
|
||||
|
||||
@ -273,6 +274,10 @@ func (c *grpcUserAuthClient) RecordLoginBlocked(ctx context.Context, req *userv1
|
||||
return c.client.RecordLoginBlocked(ctx, req)
|
||||
}
|
||||
|
||||
func (c *grpcUserAuthClient) CheckLoginRiskIPWhitelist(ctx context.Context, req *userv1.CheckLoginRiskIPWhitelistRequest) (*userv1.CheckLoginRiskIPWhitelistResponse, error) {
|
||||
return c.client.CheckLoginRiskIPWhitelist(ctx, req)
|
||||
}
|
||||
|
||||
func (c *grpcUserAuthClient) AppHeartbeat(ctx context.Context, req *userv1.AppHeartbeatRequest) (*userv1.AppHeartbeatResponse, error) {
|
||||
return c.client.AppHeartbeat(ctx, req)
|
||||
}
|
||||
|
||||
@ -189,6 +189,9 @@ func (h *Handler) allowLoginRisk(writer http.ResponseWriter, request *http.Reque
|
||||
if !config.Enabled {
|
||||
return true
|
||||
}
|
||||
if h.loginRiskIPWhitelisted(request, input) {
|
||||
return true
|
||||
}
|
||||
if reason := config.FastGuard.blockReason(input.Language, input.Timezone); reason != "" {
|
||||
h.recordLoginBlocked(request, input, reason)
|
||||
httpkit.WriteError(writer, request, http.StatusForbidden, httpkit.CodeAuthLoginBlocked, "login blocked")
|
||||
@ -212,6 +215,28 @@ func (h *Handler) allowLoginRisk(writer http.ResponseWriter, request *http.Reque
|
||||
return true
|
||||
}
|
||||
|
||||
func (h *Handler) loginRiskIPWhitelisted(request *http.Request, input loginRiskInput) bool {
|
||||
if h.userClient == nil {
|
||||
return false
|
||||
}
|
||||
ip := strings.TrimSpace(clientIP(request))
|
||||
if ip == "" {
|
||||
return false
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(request.Context(), 500*time.Millisecond)
|
||||
defer cancel()
|
||||
resp, err := h.userClient.CheckLoginRiskIPWhitelist(ctx, &userv1.CheckLoginRiskIPWhitelistRequest{
|
||||
Meta: authRequestMetaWithLoginContext(request, "", input.Platform, input.Language, input.Timezone),
|
||||
ClientIp: ip,
|
||||
})
|
||||
if err != nil {
|
||||
// 白名单读取失败不能制造额外放行;继续执行原地区屏蔽链路,并保留日志用于排查 Redis/DB 回源异常。
|
||||
logx.Warn(request.Context(), "login_ip_whitelist_check_failed", slog.String("component", "gateway_login_risk"), slog.String("client_ip_hash", hashLoginRiskIP(ip)), slog.String("error", err.Error()))
|
||||
return false
|
||||
}
|
||||
return resp.GetWhitelisted()
|
||||
}
|
||||
|
||||
func (h *Handler) recordLoginBlocked(request *http.Request, input loginRiskInput, reason string) {
|
||||
if h.userClient == nil {
|
||||
return
|
||||
|
||||
@ -293,12 +293,14 @@ type fakeUserAuthClient struct {
|
||||
lastRefresh *userv1.RefreshTokenRequest
|
||||
lastLogout *userv1.LogoutRequest
|
||||
lastBlocked *userv1.RecordLoginBlockedRequest
|
||||
lastWhitelist *userv1.CheckLoginRiskIPWhitelistRequest
|
||||
lastAppHeartbeat *userv1.AppHeartbeatRequest
|
||||
loginPasswordCount int
|
||||
loginThirdCount int
|
||||
refreshCount int
|
||||
logoutCount int
|
||||
blockedCount int
|
||||
whitelisted bool
|
||||
loginErr error
|
||||
}
|
||||
|
||||
@ -681,6 +683,11 @@ func (f *fakeUserAuthClient) RecordLoginBlocked(_ context.Context, req *userv1.R
|
||||
return &userv1.RecordLoginBlockedResponse{Recorded: true}, nil
|
||||
}
|
||||
|
||||
func (f *fakeUserAuthClient) CheckLoginRiskIPWhitelist(_ context.Context, req *userv1.CheckLoginRiskIPWhitelistRequest) (*userv1.CheckLoginRiskIPWhitelistResponse, error) {
|
||||
f.lastWhitelist = req
|
||||
return &userv1.CheckLoginRiskIPWhitelistResponse{Whitelisted: f.whitelisted}, nil
|
||||
}
|
||||
|
||||
func (f *fakeUserAuthClient) AppHeartbeat(_ context.Context, req *userv1.AppHeartbeatRequest) (*userv1.AppHeartbeatResponse, error) {
|
||||
f.lastAppHeartbeat = req
|
||||
return &userv1.AppHeartbeatResponse{Accepted: true, HeartbeatAtMs: 1_778_000_005_000, ServerTimeMs: 1_778_000_005_000, Token: &userv1.AuthToken{
|
||||
@ -5366,6 +5373,33 @@ func TestLoginRiskFastGuardBlocksLanguage(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoginRiskIPWhitelistBypassesFastGuard(t *testing.T) {
|
||||
userClient := &fakeUserAuthClient{whitelisted: true}
|
||||
handler := NewHandler(&fakeRoomClient{}, userClient)
|
||||
handler.SetLoginRisk(LoginRiskConfig{
|
||||
Enabled: true,
|
||||
FastGuard: LoginRiskFastGuardConfig{
|
||||
BlockedLanguagePrimaryTags: []string{"zh"},
|
||||
},
|
||||
})
|
||||
router := handler.Routes(auth.NewVerifier("secret"))
|
||||
body := []byte(`{"account":"123456","password":"1234567","device_id":"ios","platform":"ios","language":"zh-CN","timezone":"America/Los_Angeles"}`)
|
||||
request := httptest.NewRequest(http.MethodPost, "/api/v1/auth/account/login", bytes.NewReader(body))
|
||||
request.Header.Set("X-Request-ID", "req-login-risk-whitelist")
|
||||
request.Header.Set("X-Forwarded-For", "203.0.113.52")
|
||||
recorder := httptest.NewRecorder()
|
||||
|
||||
router.ServeHTTP(recorder, request)
|
||||
|
||||
assertEnvelope(t, recorder, http.StatusOK, httpkit.CodeOK, "req-login-risk-whitelist")
|
||||
if userClient.loginPasswordCount != 1 || userClient.blockedCount != 0 {
|
||||
t.Fatalf("whitelisted IP must bypass fast guard: login=%d blocked=%d", userClient.loginPasswordCount, userClient.blockedCount)
|
||||
}
|
||||
if userClient.lastWhitelist == nil || userClient.lastWhitelist.GetClientIp() != "203.0.113.52" {
|
||||
t.Fatalf("whitelist check request mismatch: %+v", userClient.lastWhitelist)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoginRiskIPCacheBlocksLogin(t *testing.T) {
|
||||
userClient := &fakeUserAuthClient{}
|
||||
cache := newMemoryLoginRiskCache()
|
||||
|
||||
@ -1236,3 +1236,18 @@ CREATE TABLE IF NOT EXISTS login_risk_country_blocks (
|
||||
KEY idx_login_risk_country_blocks_enabled (app_code, enabled, country_code),
|
||||
KEY idx_login_risk_country_blocks_updated (app_code, updated_at_ms)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='登录风险国家拦截表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS login_risk_ip_whitelist (
|
||||
app_code VARCHAR(32) NOT NULL COMMENT '应用编码,用于多租户隔离',
|
||||
whitelist_id BIGINT NOT NULL AUTO_INCREMENT COMMENT '白名单 ID',
|
||||
ip_address VARCHAR(64) NOT NULL COMMENT '允许跳过登录地区屏蔽的客户端 IP',
|
||||
enabled TINYINT(1) NOT NULL DEFAULT 1 COMMENT '启用',
|
||||
created_by_user_id BIGINT NULL COMMENT '创建人用户 ID',
|
||||
updated_by_user_id BIGINT NULL COMMENT '更新人用户 ID',
|
||||
created_at_ms BIGINT NOT NULL COMMENT '创建时间,UTC epoch ms',
|
||||
updated_at_ms BIGINT NOT NULL COMMENT '更新时间,UTC epoch ms',
|
||||
PRIMARY KEY (whitelist_id),
|
||||
UNIQUE KEY uk_login_risk_ip_whitelist_ip (app_code, ip_address),
|
||||
KEY idx_login_risk_ip_whitelist_enabled (app_code, enabled, ip_address),
|
||||
KEY idx_login_risk_ip_whitelist_updated (app_code, updated_at_ms)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='登录风险 IP 白名单表';
|
||||
|
||||
@ -71,3 +71,14 @@ type LoginRiskCountryBlock struct {
|
||||
CreatedAtMs int64
|
||||
UpdatedAtMs int64
|
||||
}
|
||||
|
||||
// LoginRiskIPWhitelist 是后台维护的登录地区屏蔽 IP 白名单事实。
|
||||
// IPAddress 使用 netip 标准化后的明文地址,便于运营审计和登录入口精确匹配。
|
||||
type LoginRiskIPWhitelist struct {
|
||||
AppCode string
|
||||
WhitelistID int64
|
||||
IPAddress string
|
||||
Enabled bool
|
||||
CreatedAtMs int64
|
||||
UpdatedAtMs int64
|
||||
}
|
||||
|
||||
@ -59,10 +59,37 @@ func (c *RedisIPDecisionCache) SetRevokedSession(ctx context.Context, appCode st
|
||||
return c.client.Set(ctx, revokedSessionKey(appCode, sessionID), strings.TrimSpace(reason), ttl).Err()
|
||||
}
|
||||
|
||||
func (c *RedisIPDecisionCache) GetIPWhitelist(ctx context.Context, appCode string) ([]string, bool, error) {
|
||||
raw, err := c.client.Get(ctx, loginRiskIPWhitelistKey(appCode)).Result()
|
||||
if errors.Is(err, redis.Nil) {
|
||||
return nil, false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
var ips []string
|
||||
if err := json.Unmarshal([]byte(raw), &ips); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return ips, true, nil
|
||||
}
|
||||
|
||||
func (c *RedisIPDecisionCache) SetIPWhitelist(ctx context.Context, appCode string, ips []string, ttl time.Duration) error {
|
||||
raw, err := json.Marshal(ips)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return c.client.Set(ctx, loginRiskIPWhitelistKey(appCode), raw, ttl).Err()
|
||||
}
|
||||
|
||||
func loginRiskIPDecisionKey(appCode string, clientIPHash string) string {
|
||||
return "login_risk:ip:" + appcode.Normalize(appCode) + ":" + strings.TrimSpace(clientIPHash)
|
||||
}
|
||||
|
||||
func loginRiskIPWhitelistKey(appCode string) string {
|
||||
return "login_risk:ip_whitelist:" + appcode.Normalize(appCode)
|
||||
}
|
||||
|
||||
func revokedSessionKey(appCode string, sessionID string) string {
|
||||
return "auth:revoked_session:" + appcode.Normalize(appCode) + ":" + strings.TrimSpace(sessionID)
|
||||
}
|
||||
|
||||
@ -2,13 +2,17 @@ package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/netip"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"hyapp/pkg/appcode"
|
||||
authdomain "hyapp/services/user-service/internal/domain/auth"
|
||||
)
|
||||
|
||||
const loginRiskIPWhitelistCacheTTL = 30 * time.Second
|
||||
|
||||
// ListLoginRiskBlockedCountries 返回当前 App 实际生效的登录地区风控词表。
|
||||
// 静态配置和后台动态配置都下发给 App;App 可异步做本地风险提示,最终封禁仍以服务端 worker 为准。
|
||||
func (s *Service) ListLoginRiskBlockedCountries(ctx context.Context) ([]authdomain.LoginRiskCountryBlock, error) {
|
||||
@ -56,6 +60,54 @@ func (s *Service) ListLoginRiskBlockedCountries(ctx context.Context) ([]authdoma
|
||||
return blocks, nil
|
||||
}
|
||||
|
||||
// CheckLoginRiskIPWhitelisted 判断入口 IP 是否命中后台白名单。
|
||||
// 白名单是“跳过登录地区屏蔽”的明确运营例外,只做标准化后的单 IP 精确匹配,不扩展为网段或地区推断。
|
||||
func (s *Service) CheckLoginRiskIPWhitelisted(ctx context.Context, clientIP string) (bool, error) {
|
||||
ip := normalizeLoginRiskIP(clientIP)
|
||||
if ip == "" {
|
||||
return false, nil
|
||||
}
|
||||
ips, err := s.listLoginRiskIPWhitelist(ctx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, item := range ips {
|
||||
if item == ip {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (s *Service) listLoginRiskIPWhitelist(ctx context.Context) ([]string, error) {
|
||||
appCode := appcode.FromContext(ctx)
|
||||
if s.ipDecisionCache != nil {
|
||||
ips, ok, err := s.ipDecisionCache.GetIPWhitelist(ctx, appCode)
|
||||
if err == nil && ok {
|
||||
return normalizeLoginRiskIPList(ips), nil
|
||||
}
|
||||
}
|
||||
if s.authRepository == nil {
|
||||
return nil, nil
|
||||
}
|
||||
items, err := s.authRepository.ListEnabledLoginRiskIPWhitelist(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ips := make([]string, 0, len(items))
|
||||
for _, item := range items {
|
||||
if ip := normalizeLoginRiskIP(item.IPAddress); ip != "" {
|
||||
ips = append(ips, ip)
|
||||
}
|
||||
}
|
||||
ips = normalizeLoginRiskIPList(ips)
|
||||
if s.ipDecisionCache != nil {
|
||||
// 白名单读取结果按 App 整体缓存 30 秒;空列表也缓存,避免无配置时每次登录都回源 DB。
|
||||
_ = s.ipDecisionCache.SetIPWhitelist(ctx, appCode, ips, loginRiskIPWhitelistCacheTTL)
|
||||
}
|
||||
return ips, nil
|
||||
}
|
||||
|
||||
func appendUniqueCountryBlock(blocks []authdomain.LoginRiskCountryBlock, seen map[string]struct{}, block authdomain.LoginRiskCountryBlock) []authdomain.LoginRiskCountryBlock {
|
||||
key := strings.ToLower(block.CountryCode + "\x00" + block.Keyword)
|
||||
if _, ok := seen[key]; ok {
|
||||
@ -89,3 +141,29 @@ func (s *Service) countryBlockedByDynamicPolicy(ctx context.Context, countryCode
|
||||
}
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func normalizeLoginRiskIP(raw string) string {
|
||||
addr, err := netip.ParseAddr(strings.TrimSpace(raw))
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return addr.String()
|
||||
}
|
||||
|
||||
func normalizeLoginRiskIPList(raw []string) []string {
|
||||
seen := make(map[string]struct{}, len(raw))
|
||||
ips := make([]string, 0, len(raw))
|
||||
for _, item := range raw {
|
||||
ip := normalizeLoginRiskIP(item)
|
||||
if ip == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[ip]; ok {
|
||||
continue
|
||||
}
|
||||
seen[ip] = struct{}{}
|
||||
ips = append(ips, ip)
|
||||
}
|
||||
sort.Strings(ips)
|
||||
return ips
|
||||
}
|
||||
|
||||
@ -71,6 +71,19 @@ func normalizeLoginIPRiskWorkerOptions(options LoginIPRiskWorkerOptions) LoginIP
|
||||
func (s *Service) processLoginIPRiskJob(ctx context.Context, job authdomain.LoginIPRiskJob) error {
|
||||
policy := s.loginRiskPolicy
|
||||
nowMs := s.now().UnixMilli()
|
||||
whitelisted, err := s.CheckLoginRiskIPWhitelisted(ctx, job.ClientIP)
|
||||
if err != nil {
|
||||
logx.Warn(ctx, "login_ip_whitelist_read_failed", slog.String("component", "user_auth_risk"), slog.String("job_id", job.JobID), slog.String("client_ip_hash", job.ClientIPHash), slog.String("error", err.Error()))
|
||||
}
|
||||
if whitelisted {
|
||||
// 白名单是运营显式放行,命中后不再访问 GeoIP provider,也不写 IP blocked 决策,避免后续登录被旧地区缓存误拦。
|
||||
return s.applyLoginIPRiskDecision(ctx, job, IPDecision{
|
||||
Decision: authdomain.LoginIPRiskDecisionAllowed,
|
||||
Source: "ip_whitelist",
|
||||
CheckedAtMs: nowMs,
|
||||
ExpiresAtMs: nowMs + loginRiskIPWhitelistCacheTTL.Milliseconds(),
|
||||
}, nil, authdomain.LoginIPRiskStatusCompleted)
|
||||
}
|
||||
if cached, ok := s.freshCachedIPDecision(ctx, job); ok {
|
||||
return s.applyLoginIPRiskDecision(ctx, job, cached, nil, authdomain.LoginIPRiskStatusSkipped)
|
||||
}
|
||||
|
||||
@ -78,6 +78,8 @@ type AuthRepository interface {
|
||||
CreateLoginIPRiskDecision(ctx context.Context, decision authdomain.LoginIPRiskDecision) error
|
||||
// ListEnabledLoginRiskCountryBlocks 返回当前 App 的动态屏蔽国家词表。
|
||||
ListEnabledLoginRiskCountryBlocks(ctx context.Context) ([]authdomain.LoginRiskCountryBlock, error)
|
||||
// ListEnabledLoginRiskIPWhitelist 返回当前 App 的登录地区屏蔽 IP 白名单。
|
||||
ListEnabledLoginRiskIPWhitelist(ctx context.Context) ([]authdomain.LoginRiskIPWhitelist, error)
|
||||
// MarkLoginAuditBlocked 把登录成功后被异步风控撤销的审计行更新成阻断态。
|
||||
MarkLoginAuditBlocked(ctx context.Context, requestID string, userID int64, countryCode string, blockReason string) error
|
||||
}
|
||||
@ -165,6 +167,8 @@ type IPDecisionCache interface {
|
||||
GetIPDecision(ctx context.Context, appCode string, clientIPHash string) (IPDecision, bool, error)
|
||||
SetIPDecision(ctx context.Context, appCode string, clientIPHash string, decision IPDecision, ttl time.Duration) error
|
||||
SetRevokedSession(ctx context.Context, appCode string, sessionID string, reason string, ttl time.Duration) error
|
||||
GetIPWhitelist(ctx context.Context, appCode string) ([]string, bool, error)
|
||||
SetIPWhitelist(ctx context.Context, appCode string, ips []string, ttl time.Duration) error
|
||||
}
|
||||
|
||||
// IPDecision 是 Redis 中 IP 决策 JSON 的领域投影。
|
||||
|
||||
@ -23,10 +23,11 @@ type memoryDecisionCache struct {
|
||||
mu sync.Mutex
|
||||
ip map[string]authservice.IPDecision
|
||||
revoked map[string]string
|
||||
ips map[string][]string
|
||||
}
|
||||
|
||||
func newMemoryDecisionCache() *memoryDecisionCache {
|
||||
return &memoryDecisionCache{ip: make(map[string]authservice.IPDecision), revoked: make(map[string]string)}
|
||||
return &memoryDecisionCache{ip: make(map[string]authservice.IPDecision), revoked: make(map[string]string), ips: make(map[string][]string)}
|
||||
}
|
||||
|
||||
func (c *memoryDecisionCache) GetIPDecision(_ context.Context, appCode string, clientIPHash string) (authservice.IPDecision, bool, error) {
|
||||
@ -50,6 +51,20 @@ func (c *memoryDecisionCache) SetRevokedSession(_ context.Context, appCode strin
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *memoryDecisionCache) GetIPWhitelist(_ context.Context, appCode string) ([]string, bool, error) {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
ips, ok := c.ips[appCode]
|
||||
return append([]string(nil), ips...), ok, nil
|
||||
}
|
||||
|
||||
func (c *memoryDecisionCache) SetIPWhitelist(_ context.Context, appCode string, ips []string, _ time.Duration) error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
c.ips[appCode] = append([]string(nil), ips...)
|
||||
return nil
|
||||
}
|
||||
|
||||
type sequenceIDGenerator struct {
|
||||
// values 是测试预设 user_id 序列。
|
||||
values []int64
|
||||
@ -552,6 +567,73 @@ func TestLoginIPRiskWorkerRevokesBlockedCountrySession(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoginIPRiskIPWhitelistBypassesBlockedCountry(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
repository := mysqltest.NewRepository(t)
|
||||
seedCountry(t, repository, "CN")
|
||||
now := time.UnixMilli(1000)
|
||||
if _, err := repository.RawDB().ExecContext(ctx, `
|
||||
INSERT INTO login_risk_ip_whitelist (
|
||||
app_code, ip_address, enabled, created_at_ms, updated_at_ms
|
||||
)
|
||||
VALUES ('lalu', '203.0.113.80', 1, 1000, 1000)
|
||||
`); err != nil {
|
||||
t.Fatalf("seed ip whitelist failed: %v", err)
|
||||
}
|
||||
cache := newMemoryDecisionCache()
|
||||
authSvc := newAuthService(repository, &now, []int64{900024}, []string{"100024"},
|
||||
authservice.WithLoginRiskPolicy(authservice.LoginRiskPolicy{
|
||||
Enabled: true,
|
||||
UnsupportedCountries: []string{"CN"},
|
||||
BlockedTTL: time.Hour,
|
||||
AllowedTTL: time.Hour,
|
||||
UnknownTTL: time.Minute,
|
||||
ProviderTimeout: 50 * time.Millisecond,
|
||||
ProviderNames: []string{"edge_country"},
|
||||
DenylistTTL: time.Hour,
|
||||
}),
|
||||
authservice.WithIPDecisionCache(cache),
|
||||
authservice.WithIPGeoProviders(authservice.BuildIPGeoProviders([]string{"edge_country"})),
|
||||
)
|
||||
|
||||
token, _, err := authSvc.LoginThirdParty(ctx, "wechat", "openid-risk-whitelist", thirdPartyRegistration("ios"), authservice.Meta{
|
||||
AppCode: "lalu",
|
||||
RequestID: "req-risk-whitelist",
|
||||
ClientIP: "203.0.113.80",
|
||||
CountryByIP: "CN",
|
||||
Platform: "ios",
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("LoginThirdParty failed: %v", err)
|
||||
}
|
||||
|
||||
now = time.UnixMilli(2000)
|
||||
processed, err := authSvc.ProcessLoginIPRiskBatch(ctx, authservice.LoginIPRiskWorkerOptions{WorkerID: "risk-whitelist-test", LockTTL: time.Second, BatchSize: 10})
|
||||
if err != nil || processed != 1 {
|
||||
t.Fatalf("risk worker mismatch: processed=%d err=%v", processed, err)
|
||||
}
|
||||
if _, err := authSvc.RefreshToken(ctx, token.RefreshToken, "dev-ios", authservice.Meta{AppCode: "lalu", RequestID: "req-refresh-whitelist"}); err != nil {
|
||||
t.Fatalf("whitelisted IP must keep refresh session active: %v", err)
|
||||
}
|
||||
if _, ok := cache.ip["lalu:"+authservice.HashLoginRiskIP("203.0.113.80")]; ok {
|
||||
t.Fatalf("whitelisted IP must not write ordinary IP decision cache")
|
||||
}
|
||||
if got := cache.ips["lalu"]; len(got) != 1 || got[0] != "203.0.113.80" {
|
||||
t.Fatalf("ip whitelist cache mismatch: %+v", got)
|
||||
}
|
||||
var decision string
|
||||
if err := repository.RawDB().QueryRowContext(ctx, `
|
||||
SELECT decision
|
||||
FROM login_ip_risk_jobs
|
||||
WHERE app_code = 'lalu' AND request_id = 'req-risk-whitelist'
|
||||
`).Scan(&decision); err != nil {
|
||||
t.Fatalf("query risk job failed: %v", err)
|
||||
}
|
||||
if decision != "allowed" {
|
||||
t.Fatalf("whitelisted risk job decision mismatch: %s", decision)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoginIPRiskWorkerRevokesRotatedSameDeviceSessions(t *testing.T) {
|
||||
// 复现线上竞态:登录后风控任务尚未完成,客户端连续 refresh 轮换 session。
|
||||
// blocked 决策落地时必须沿源 session 的同设备登录链路清掉后续 session,否则最新 access/refresh 仍能继续使用。
|
||||
|
||||
@ -131,6 +131,36 @@ func (r *Repository) ListEnabledLoginRiskCountryBlocks(ctx context.Context) ([]a
|
||||
return blocks, nil
|
||||
}
|
||||
|
||||
func (r *Repository) ListEnabledLoginRiskIPWhitelist(ctx context.Context) ([]authdomain.LoginRiskIPWhitelist, error) {
|
||||
rows, err := r.db.QueryContext(ctx, `
|
||||
SELECT app_code, whitelist_id, ip_address, enabled, created_at_ms, updated_at_ms
|
||||
FROM login_risk_ip_whitelist
|
||||
WHERE app_code = ? AND enabled = 1
|
||||
ORDER BY ip_address ASC, whitelist_id ASC
|
||||
`, appcode.FromContext(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
items := make([]authdomain.LoginRiskIPWhitelist, 0)
|
||||
for rows.Next() {
|
||||
var item authdomain.LoginRiskIPWhitelist
|
||||
if err := rows.Scan(&item.AppCode, &item.WhitelistID, &item.IPAddress, &item.Enabled, &item.CreatedAtMs, &item.UpdatedAtMs); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
item.IPAddress = strings.TrimSpace(item.IPAddress)
|
||||
if item.IPAddress == "" {
|
||||
continue
|
||||
}
|
||||
items = append(items, item)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return items, nil
|
||||
}
|
||||
|
||||
type loginIPRiskJobScanner interface {
|
||||
Scan(dest ...any) error
|
||||
}
|
||||
|
||||
@ -394,6 +394,11 @@ func (r *Repository) ListEnabledLoginRiskCountryBlocks(ctx context.Context) ([]a
|
||||
return r.Repository.AuthRepository().ListEnabledLoginRiskCountryBlocks(ctx)
|
||||
}
|
||||
|
||||
// ListEnabledLoginRiskIPWhitelist 让测试 wrapper 继续满足认证风控接口。
|
||||
func (r *Repository) ListEnabledLoginRiskIPWhitelist(ctx context.Context) ([]authdomain.LoginRiskIPWhitelist, error) {
|
||||
return r.Repository.AuthRepository().ListEnabledLoginRiskIPWhitelist(ctx)
|
||||
}
|
||||
|
||||
// MarkLoginAuditBlocked 让测试 wrapper 继续满足认证接口。
|
||||
func (r *Repository) MarkLoginAuditBlocked(ctx context.Context, requestID string, userID int64, countryCode string, blockReason string) error {
|
||||
return r.Repository.AuthRepository().MarkLoginAuditBlocked(ctx, requestID, userID, countryCode, blockReason)
|
||||
|
||||
@ -212,6 +212,16 @@ func (s *Server) RecordLoginBlocked(ctx context.Context, req *userv1.RecordLogin
|
||||
return &userv1.RecordLoginBlockedResponse{Recorded: recorded}, nil
|
||||
}
|
||||
|
||||
// CheckLoginRiskIPWhitelist 判断登录入口 IP 是否命中后台白名单;gateway 用它在快拦截前保留运营例外。
|
||||
func (s *Server) CheckLoginRiskIPWhitelist(ctx context.Context, req *userv1.CheckLoginRiskIPWhitelistRequest) (*userv1.CheckLoginRiskIPWhitelistResponse, error) {
|
||||
ctx = contextWithApp(ctx, req.GetMeta())
|
||||
whitelisted, err := s.authSvc.CheckLoginRiskIPWhitelisted(ctx, req.GetClientIp())
|
||||
if err != nil {
|
||||
return nil, xerr.ToGRPCError(err)
|
||||
}
|
||||
return &userv1.CheckLoginRiskIPWhitelistResponse{Whitelisted: whitelisted}, nil
|
||||
}
|
||||
|
||||
// AppHeartbeat 刷新当前 App 登录会话最近在线时间,不创建房间或麦位 presence。
|
||||
func (s *Server) AppHeartbeat(ctx context.Context, req *userv1.AppHeartbeatRequest) (*userv1.AppHeartbeatResponse, error) {
|
||||
ctx = contextWithApp(ctx, req.GetMeta())
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user