HZERO微服务平台07: 代码分析之登录日志、验证码登录、jwt token等

登陆日志

分析

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, 注册了JwtTokenFilterJwtTokenExtractortokenStoretokenServices

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.oauthJwtKeyCoreProperties#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);
原文地址:https://www.cnblogs.com/QIAOXINGXING001/p/15594752.html