登陆日志
分析
LoginAuditProcessor
记录登陆日志, 只能记录隐式流程、手机号跳转;
新增了AuthenticationSuccessEventListener
, 用来记录password流程;
2021-04-02: client_credentials
还不能记录
授权码模式未测试;
代码流程
从下向上找的分析过程:
表: hpfm_audit_login
实体: AuditLogin
记录日志都要调用Repository, 全局搜索: auditLoginRepository.
AuditLoginServiceImpl#login
AuditLoginServiceImpl#addLoginRecord
LoginAuditProcessor#process
两个位置:
1. AuthorizationEndpoint#getImplicitGrantResponse
2. LoginTokenService#createAccessToken
LoginTokenService#loginForToken
- UserLoginServiceImpl#loginMobileForToken
- UserLoginServiceImpl#loginOpenForToken
手机验证码登陆
地址: /login/sms
SmsAuthenticationFilter#attemptAuthentication
ProviderManager#authenticate
SmsAuthenticationProvider#authenticate
SmsAuthenticationProvider#additionalAuthenticationChecks
CaptchaMessageHelper#checkCaptcha
CaptchaMessageHelper#checkCaptchaWithNumber
redis db3, 数据:
hoth:captcha:user_type_p:default:code:2649ccbfb2c045c2907790b82107e172
可以不检查验证码:
//测试时禁用验证功能,不验证验证码是否正确
//hzero.captcha.testDisable
CaptchaMessageHelper#checkCaptchaWithNumber
captchaProperties.isTestDisable()
手机验证码获取token
调用方法
手机短信登陆 - OAuth Token API
{{gateway}}/oauth/token/mobile?grant_type=implicit&client_id=localhost&client_secret=secret&phone=18009908012&source_type=app&device_id=123456789&captcha=150217&captchaKey=56a7ef80d1f64834a7ab664daa248f38
代码流程
LoginController#loginMobileToken
UserLoginServiceImpl#loginMobileForToken
LoginTokenService#loginForToken
// 封装请求参数
Authentication authRequest = attemptAuthentication(request);
MobileLoginTokenService#attemptAuthentication
SmsAuthenticationToken authRequest = new SmsAuthenticationToken(mobile); //mobile是手机号
authRequest.setDetails(authenticationDetailsSource.buildDetails(request)); //验证码、key等参数在detail里
Authentication authentication = authenticationProvider.authenticate(authRequest);
SmsAuthenticationProvider#authenticate
SmsAuthenticationProvider#additionalAuthenticationChecks
CaptchaResult captchaResult = captchaMessageHelper.checkCaptcha(captchaKey, inputCaptcha, mobile, getUserType(authentication),businessScope, IospService.Oauth.CODE, false);
CaptchaMessageHelper#checkCaptchaWithNumber //验证验证码 hzero-starter-redis.jar
jwt token、@EnableChoerodonResourceServer
@EnableChoerodonResourceServer
注解
作用是: 开启资源认证, 解析jwt token、设置用户信息;
实现方式:
这个注解的作用就是导入配置类:@Import({ChoerodonResourceServerConfiguration.class})
配置类继承自spring security的配置adapter:
public class ChoerodonResourceServerConfiguration extends WebSecurityConfigurerAdapter {
...
配置类里配置了HttpSecurity
, 注册了JwtTokenFilter
、JwtTokenExtractor
、tokenStore
、tokenServices
等
JwtTokenFilter 过滤器
@EnableChoerodonResourceServer
的一个重要功能是添加了JwtTokenFilter
过滤器;JwtTokenFilter
的作用是解析jwt_token
header, 设置用户信息到SecurityContextHolder
;JwtTokenFilter
的作用路径默认是/v1/*
, 可以如下配置:
hzero:
resource:
# JwtTokenFilter应用的路径; 注意是/*不是/**
pattern: /v1/*,/api/*
# JwtTokenFilter跳过的路径; 只能精确匹配
skip-path: /v2/choerodon/api-docs
# 如果设置了context-path,需要设置为
# skip-path: ${server.servlet.context-path}/v2/choerodon/api-docs
pattern
的格式是servlet的filter的格式, /v1/*/user
无效的, 详见:
我的笔记: spring web/mvc topic .md
jwt token的密钥
设置密钥:
ChoerodonResourceServerConfiguration#accessTokenConverter
converter.setSigningKey(properties.getOauthJwtKey()); //默认是hzero
这个拦截器可以生成token:
(restTemplate、feignClient的请求会自动添加jwt_token header)
...core.net.RequestHeaderCopyInterceptor#intercept
token = OAUTH_TOKEN_PREFIX + JwtHelper.encode(objectMapper.writeValueAsString(details.getDecodedDetails()), signer).getEncoded();
默认使用HMACSHA256(HS256)算法, 是对称加密, 把key写到了配置文件hzero.oauthJwtKey
、CoreProperties#oauthJwtKey
;
HS256 使用同一个「secret_key」进行签名与验证(对称加密)。一旦 secret_key 泄漏,就毫无安全性可言了。
- 因此 HS256 只适合集中式认证,签名和验证都必须由可信方进行。
- 传统的单体应用广泛使用这种算法,但是请不要在任何分布式的架构中使用它!
JWT 签名算法 HS256、RS256 及 ES256 及密钥生成 - 於清樂 - 博客园
自定义client secret错误时的返回方式
client secret错误时会返回302, 重定向到登录页, 希望改造为返回401, 提示json格式的错误信息;
解决方式: 覆写hzero-oauth定义的SsoAuthenticationEntryPoint
调用过程:
BasicAuthenticationFilter //校验client secret, 校验失败
this.authenticationEntryPoint.commence(request, response, failed);
BasicAuthenticationEntryPoint#commence //设置401
ExceptionTranslationFilter#doFilter //捕获filter的异常
ExceptionTranslationFilter#handleSpringSecurityException
ExceptionTranslationFilter#sendStartAuthentication
authenticationEntryPoint.commence(request, response, reason);
SsoAuthenticationEntryPoint#commence
response.sendRedirect(redirectUrl);