github.com/infraboard/keyauth@v0.8.1/apps/token/security/checker.go (about) 1 package security 2 3 import ( 4 "context" 5 "fmt" 6 "time" 7 8 "github.com/infraboard/mcube/app" 9 "github.com/infraboard/mcube/cache" 10 "github.com/infraboard/mcube/exception" 11 "github.com/infraboard/mcube/logger" 12 "github.com/infraboard/mcube/logger/zap" 13 14 "github.com/infraboard/keyauth/apps/domain" 15 "github.com/infraboard/keyauth/apps/ip2region" 16 "github.com/infraboard/keyauth/apps/session" 17 "github.com/infraboard/keyauth/apps/token" 18 "github.com/infraboard/keyauth/apps/user" 19 ) 20 21 // NewChecker todo 22 func NewChecker() (Checker, error) { 23 c := cache.C() 24 if c == nil { 25 return nil, fmt.Errorf("denpence cache service is nil") 26 } 27 28 return &checker{ 29 domain: app.GetGrpcApp(domain.AppName).(domain.ServiceServer), 30 user: app.GetGrpcApp(user.AppName).(user.ServiceServer), 31 session: app.GetGrpcApp(session.AppName).(session.ServiceServer), 32 cache: c, 33 ip2Regoin: app.GetInternalApp(ip2region.AppName).(ip2region.Service), 34 log: zap.L().Named("Login Security"), 35 }, nil 36 } 37 38 type checker struct { 39 domain domain.ServiceServer 40 user user.ServiceServer 41 session session.ServiceServer 42 cache cache.Cache 43 ip2Regoin ip2region.Service 44 log logger.Logger 45 } 46 47 func (c *checker) MaxFailedRetryCheck(ctx context.Context, req *token.IssueTokenRequest) error { 48 ss := c.getOrDefaultSecuritySettingWithUser(ctx, req.Username) 49 if !ss.LoginSecurity.RetryLock { 50 c.log.Debugf("retry lock check disabled, don't check") 51 return nil 52 } 53 c.log.Debugf("max failed retry lock check enabled, checking ...") 54 55 var count uint32 56 err := c.cache.Get(req.AbnormalUserCheckKey(), &count) 57 if err != nil { 58 c.log.Errorf("get key %s from cache error, %s", req.AbnormalUserCheckKey(), err) 59 } 60 61 rc := ss.LoginSecurity.RetryLockConfig 62 c.log.Debugf("retry times: %d, retry limite: %d", count, rc.RetryLimite) 63 if count+1 >= rc.RetryLimite { 64 return fmt.Errorf("登录失败次数过多, 请%d分钟后重试", rc.LockedMinite) 65 } 66 67 return nil 68 } 69 70 func (c *checker) UpdateFailedRetry(ctx context.Context, req *token.IssueTokenRequest) error { 71 ss := c.getOrDefaultSecuritySettingWithUser(ctx, req.Username) 72 if !ss.LoginSecurity.RetryLock { 73 c.log.Debugf("retry lock check disabled, don't check") 74 return nil 75 } 76 77 c.log.Debugf("update failed retry count, check key: %s", req.AbnormalUserCheckKey()) 78 79 var count int 80 if err := c.cache.Get(req.AbnormalUserCheckKey(), &count); err == nil { 81 // 之前已经登陆失败过 82 err := c.cache.Put(req.AbnormalUserCheckKey(), count+1) 83 if err != nil { 84 c.log.Errorf("set key %s to cache error, %s", req.AbnormalUserCheckKey()) 85 } 86 } else { 87 // 首次登陆失败 88 err := c.cache.PutWithTTL( 89 req.AbnormalUserCheckKey(), 90 count+1, 91 ss.LoginSecurity.RetryLockConfig.LockedMiniteDuration(), 92 ) 93 if err != nil { 94 c.log.Errorf("set key %s to cache error, %s", req.AbnormalUserCheckKey()) 95 } 96 } 97 return nil 98 } 99 100 func (c *checker) OtherPlaceLoggedInChecK(ctx context.Context, tk *token.Token) error { 101 ss := c.getOrDefaultSecuritySettingWithDomain(ctx, tk.Account, tk.Domain) 102 if !ss.LoginSecurity.ExceptionLock { 103 c.log.Debugf("exception check disabled, don't check") 104 return nil 105 } 106 107 if !ss.LoginSecurity.ExceptionLockConfig.OtherPlaceLogin { 108 c.log.Debugf("other place login check disabled, don't check") 109 return nil 110 } 111 112 c.log.Debugf("other place login check enabled, checking ...") 113 114 // 查询当前登陆IP地域 115 rip := tk.GetRemoteIP() 116 c.log.Debugf("query remote ip: %s location ...", rip) 117 login, err := c.ip2Regoin.LookupIP(rip) 118 if err != nil { 119 c.log.Errorf("lookup ip %s error, %s, skip OtherPlaceLoggedInChecK", rip, err) 120 return nil 121 } 122 123 // 查询出用户上次登陆的地域 124 queryReq := session.NewQueryUserLastSessionRequest(tk.Account) 125 us, err := c.session.QueryUserLastSession(ctx, queryReq) 126 if err != nil { 127 if exception.IsNotFoundError(err) { 128 c.log.Debugf("user %s last login session not found", tk.Account) 129 return nil 130 } 131 132 return err 133 } 134 135 if us.IpInfo == nil { 136 c.log.Debugf("last login session no ip info found, skip OtherPlaceLoggedInChecK") 137 return nil 138 } 139 140 // city为0 表示内网IP, 不错异地登录校验 141 if login.CityID == 0 || us.IpInfo.CityId == 0 { 142 c.log.Warnf("city id is 0, 内网IP skip OtherPlaceLoggedInChecK") 143 return nil 144 } 145 146 if us != nil { 147 c.log.Debugf("user last login city: %s (%d)", us.IpInfo.City, us.IpInfo.CityId) 148 if login.CityID != us.IpInfo.CityId { 149 return fmt.Errorf("异地登录, 请输入验证码后再次提交") 150 } 151 } 152 return nil 153 } 154 155 func (c *checker) NotLoginDaysChecK(ctx context.Context, tk *token.Token) error { 156 ss := c.getOrDefaultSecuritySettingWithUser(ctx, tk.Account) 157 if !ss.LoginSecurity.ExceptionLock { 158 c.log.Debugf("exception check disabled, don't check") 159 return nil 160 } 161 c.log.Debugf("not login days check enabled, checking ...") 162 163 // 查询出用户上次登陆的地域 164 queryReq := session.NewQueryUserLastSessionRequest(tk.Account) 165 us, err := c.session.QueryUserLastSession(ctx, queryReq) 166 if err != nil { 167 if exception.IsNotFoundError(err) { 168 c.log.Debugf("user %s last login session not found", tk.Account) 169 return nil 170 } 171 172 return err 173 } 174 175 if us != nil { 176 days := uint32(time.Now().Sub(time.Unix(us.LoginAt/1000, 0)).Hours() / 24) 177 c.log.Debugf("user %d days not login", days) 178 maxDays := ss.LoginSecurity.ExceptionLockConfig.NotLoginDays 179 if days > maxDays { 180 return fmt.Errorf("user not login days %d", days) 181 } 182 c.log.Debugf("not login days check passed, days: %d, max days: %d", days, maxDays) 183 } 184 185 return nil 186 } 187 188 func (c *checker) IPProtectCheck(ctx context.Context, req *token.IssueTokenRequest) error { 189 ss := c.getOrDefaultSecuritySettingWithUser(ctx, req.Username) 190 if !ss.LoginSecurity.IpLimite { 191 c.log.Debugf("ip limite check disabled, don't check") 192 return nil 193 } 194 195 c.log.Debugf("ip limite check enabled, checking ...") 196 197 return nil 198 } 199 200 func (c *checker) getOrDefaultSecuritySettingWithUser(ctx context.Context, account string) *domain.SecuritySetting { 201 ss := domain.NewDefaultSecuritySetting() 202 u, err := c.user.DescribeAccount(ctx, user.NewDescriptAccountRequestWithAccount(account)) 203 if err != nil { 204 c.log.Errorf("get user account error, %s, use default setting to check", err) 205 return ss 206 } 207 208 return c.getOrDefaultSecuritySettingWithDomain(ctx, u.Account, u.Domain) 209 } 210 211 func (c *checker) getOrDefaultSecuritySettingWithDomain(ctx context.Context, account, domainName string) *domain.SecuritySetting { 212 ss := domain.NewDefaultSecuritySetting() 213 d, err := c.domain.DescribeDomain(ctx, domain.NewDescribeDomainRequestWithName(domainName)) 214 if err != nil { 215 c.log.Errorf("get domain error, %s, use default setting to check", err) 216 return ss 217 } 218 219 return d.SecuritySetting 220 }