spring security主要分为两部分,认证(authentication)和授权(authority)。
这一篇主要是认证部分,它由 ProviderManager(AuthenticationManager)实现。具体层次结构如下:
AuthenticationManager说明
认证的核心就是登录,这里简单介绍下security自定义token登录的实现逻辑,同时兼容用户名密码登录。
大体分为以下几个步骤:
自定义AuthenticationToken实现: 不同登录方式使用不同的token
自定义AuthenticationProcessingFilter实现:用来过滤指定的登录方式,生成对应的自定义AuthenticationToken实现
自定义AuthenticationProvider实现:针对不同登录方式提供的认证逻辑
自定义UserDetailsService实现:自定义用户信息查询服务
WebSecurityConfigurerAdapter声明:security信息配置,将前面的自定义对象注入到流程中。
github代码路径
注:仅说明实现方式,逻辑简化处理。
用户名密码登录:
http://127.0.0.1:9999/
admin imtoken官网下载的网站在哪里 123456
user 123456
token登录:
user登录:
http://127.0.0.1:9999/tokenLogin?token=loginToken_user
admin登录:
http://127.0.0.1:9999/tokenLogin?token=loginToken_admin
spring security主要分为两部分,认证(authentication)和授权(authority)。
这一篇主要是授权部分,它由FilterSecurityInterceptor逻辑拦截处理,具体通过AccessDecisionManager实现。
系统提供了三种实现方式:
AffirmativeBased(spring security默认使用):
只要有投通过(ACCESS_GRANTED)票,则直接判为通过。
如果没有投通过票且反对(ACCESS_DENIED)票在1个及其以上的,则直接判为不通过。
ConsensusBased(少数服从多数):
通过的票数大于反对的票数则判为通过;通过的票数小于反对的票数则判为不通过;
通过的票数和反对的票数相等,则可根据配置allowIfEqualGrantedDeniedDecisions(默认为true)进行判断是否通过。
UnanimousBased(反对票优先):
无论多少投票者投了多少通过(ACCESS_GRANTED)票,只要有反对票(ACCESS_DENIED),那都判为不通过;如果没有反对票且有投票者投了通过票,那么就判为通过.
这三种方式都包含了一个AccessDecisionManager(权限控制处理)和多个AccessDecisionVoter(投票项)。
系统默认提供的是基于ROLE(角色)的权限,这里自定义一下,处理 url + httpMethod 方式的权限拦截。
有三种方式可以实现:
方式一:
通过.access 方式实现。
步骤:
1. 自定义MyAuthService:实际权限校验服务
2. WebSecurityConfigurerAdapter配置:注入自定义校验服务
方式二:
通过.accessDecisionManager,覆盖AccessDecisionManager方式实现。
步骤:
1. 自定义AccessDecisionManager: 实现授权逻辑校验。
2. WebSecurityConfigurerAdapter配置:注入自定义AccessDecisionManager
方式三:
通过 添加AccessDecisionVoter投票项处理。这种兼容默认ROLE的AffirmativeBased实现
步骤:
1. 自定义AccessDecisionVoter: 实现授权投票逻辑
2. WebSecurityConfigurerAdapter配置:注入自定义AccessDecisionVoter
github代码路径
user 通过角色授权(ROLE),
admin 通过自定义投票项 UrlMatchVoter 授权。
由于AuthenticationManager使用的是默认的AffirmativeBased,所以只要有一个通过,则说明有权限。
核心关注FilterChainProxy的生成。
1、为webSecurity设置webSecurityConfigurers
org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration#setFilterChainProxySecurityConfigurer
通过#{@autowiredWebSecurityConfigurersIgnoreParents.getWebSecurityConfigurers()}查找WebSecurityConfigurer.class类型的bean,我们自定义的SecurityConfig 就是。
2、生成filter chain
2.1 bean声明,最终返回springSecurityFilterChain
org.springframework.security.config.annotation.web.configuration.WebSecurityConfiguration#springSecurityFilterChain
3、webSecurity build操作
org.springframework.security.config.annotation.AbstractConfiguredSecurityBuilder#doBuild
3.1 根据自定义的WebSecurityConfigurerAdapter进行build操作,我们这里是SecurityConfig。SecurityConfig的init过程中见第4步骤说明。
3.2 调用performBuild
生成filter chain,包括FilterChainProxy
3.2.1 FilterChainProxy包含两部分,一部分是忽略请求列表,每一个配置url就是一个DefaultSecurityFilterChain;一部分是需要鉴权的chain,包含httpSecurity filter列表,是核心功能。
filters 在请求时候根据请求信息动态匹配。
3.2.2 部分filter说明如下
https://docs.spring.io/spring-security/site/docs/4.2.2.RELEASE/reference/htmlsingle/#filter-security-interceptor
Table 6.1. Standardimtoken钱包的官网的下载的网站怎么找 Filter Aliases and Ordering
4、WebSecurityConfigurerAdapter init操作
org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter#init
4.1 生成AuthenticationManager, 执行自定义configure(localConfigureAuthenticationBldr)
4.1.1 disableLocalConfigureAuthenticationBldr为false
localConfigureAuthenticationBldr也是一个SecurityBuilder,构造返回ProviderManagement,包含多个AuthenticationProvider,用于登录鉴权处理,通过自定义SecurityConfig configure(AuthenticationManagerBuilder auth) 追加AuthenticationProvider。
4.1.2 disableLocalConfigureAuthenticationBldr为true
该逻辑中,走authenticationConfiguration逻辑,如果没有AuthenticationProvider bean,会创建DaoAuthenticationProvider。
4.2 执行自定义 configure(http) ,追加http相关配置,并将SecurityConfigurer追加到configurers集合中,如http中.logout()就会创建一个LogoutConfigurer放到集合中。
这些配置最终会生成filter,filter顺序是固定的,org.springframework.security.config.annotation.web.builders.FilterComparator#FilterComparator中存放了初始顺序。
4.3 最终追加http到web的securityFilterChainBuilders,用于后续filter生成等处理。
5、WebSecurityConfigurerAdapter configure 操作
该操作默认空操作,可以修改WebSecurity相关逻辑。
6、spring boot FilterChainProxy自动注入
org.springframework.boot.autoconfigure.security.SecurityFilterAutoConfiguration#securityFilterChainRegistration 自动注入springSecurityFilterChain filter,也就是FilterChainProxy
此处以我们前面demo中的用户名密码登录作为例子进行说明。
1、MethodSecurityInterceptor 流程与 FilterSecurityInterceptor 类似。
2、demo中的自定义AuthenticationProcessingFilter(MyTokenAuthenticationFilter) 在UsernamePasswordAuthenticationFilter之前,实现token方式登录。
OAuth 2.0定义了五种授权方式。
authorization_code:授权码类型,授权系统针对登录用户下发code,应用系统拿着code去授权系统换取token。
implicit:隐式授权类型。authorization_code的简化类型,授权系统针对登录系统直接下发token,302 跳转到应用系统url。
password:资源所有者(即用户)密码类型。应用系统采集到用户名密码,调用授权系统获取token。
client_credentials:客户端凭据(客户端ID以及Key)类型。没有用户参与,应用系统单纯的使用授权系统分配的凭证访问授权系统。
refresh_token:通过授权获得的刷新令牌 来获取 新的令牌。
自动授权client
client_id=client_id
client_secret=client_secret
非自动授权client
client_id=client2
client_secret=client2
clientId:(必须的)用来标识客户的Id。
secret:(需要值得信任的客户端)客户端安全码,如果有的话。
scope:用来限制客户端的访问范围,如果为空(默认)的话,那么客户端拥有全部的访问范围。
authorizedGrantTypes:此客户端可以使用的授权类型,默认为空。
authorities:此客户端可以使用的权限(基于Spring Security authorities)。
jti:TOKEN_ID ,refreshToken标识
ati:ACCESS_TOKEN_ID,accessToken 标识
/oauth/authorize:授权端点。
/oauth/token:令牌端点,获取token。
/oauth/confirm_access:用户确认授权提交端点。
/oauth/error:授权服务错误信息端点。
/oauth/check_token:用于资源服务访问的令牌解析端点。
/oauth/token_key:提供公有密匙的端点,如果你使用JWT(RSA)令牌的话。
github代码地址
1、请求授权:
2、使用code换取token:
获取token:
根据用户名、密码换取token
请求获取token
查看token
资源服务:提供资源访问
认证授权服务:提供认证和授权服务
客户端:请求资源服务的OAuth2 客户端
应用系统:提供应用能力的系统,在单点登录sso场景下,每一个需要认证授权服务认证授权的系统,就是一个应用系统。
spring security oauth2 提供了四个常用注解,来辅助oauth2功能的实现。
1、@EnableOAuth2Client:客户端,提供OAuth2RestTemplate,用于客户端访问资源服务。
简要步骤:客户端访问资源->客户端发现没有资源访问token->客户端根据授权类型生成跳转url->浏览器 302 到认证授权服务进行认证、授权。
2、@EnableOAuth2Sso:应用系统,使用远端认证授权服务,替换应用自身的用户登录鉴权security逻辑,实现功能。
简要步骤:访问应用系统资源-> 应用系统发现未登录-> 302 跳转到登录页面(登录页面地址已经与获取token逻辑自动关联)-> 应用系统发现符合获取token条件,根据授权类型拼装url->302 跳转到认证授权地址(认证授权服务提供)进行认证、授权。
3、@EnableAuthorizationServer:认证授权服务,提供用于获取token,解析token相关功能,实现认证、授权功能。
具体见 Spring Security 文章目录中的 Spring Cloud OAuth2 五种授权方式介绍。
4、@EnableResourceServer:资源服务,提供基于token的资源访问功能。
1、@EnableOAuth2Client
@Import(OAuth2ClientConfiguration.class)
2、OAuth2ClientConfiguration说明
2.1、OAuth2ClientContextFilter bean 声明
OAuth2 client的Security filter,拦截请求,针对UserRedirectRequiredException做redirect操作。
实际请求由OAuth2RestTemplate控制权限跳转。
2.2、AccessTokenRequest bean 声明
request scope的bean,包装access token请求所需参数。
2.3、oauth2ClientContext bean 声明
session scope 的bean,是默认的OAuth 2 security context(DefaultOAuth2ClientContext)。
1、spring boot :
通过org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2RestOperationsConfiguration.SessionScopedConfiguration#oauth2ClientFilterRegistration实现filter声明。
通过OAuth2ProtectedResourceDetailsConfiguration自定义认证类型。
1、@EnableOAuth2Sso
@Import({ OAuth2SsoDefaultConfiguration.class, OAuth2SsoCustomConfiguration.class,
ResourceServerTokenServicesConfiguration.class })
@EnableOAuth2Client
@EnableConfigurationProperties(OAuth2SsoProperties.class)
2、OAuth2SsoDefaultConfiguration说明:
如果不存在 带@EnableOAuth2Sso注解的WebSecurityConfigurerAdapter声明,当前类创建一个默认的拦截所有请求的WebSecurityConfigurerAdapter。
3、OAuth2SsoCustomConfiguration说明:
如果存在 带@EnableOAuth2Sso注解的WebSecurityConfigurerAdapter声明,生成当前bean。
3.1、重写WebSecurityConfigurerAdapter的init方法:
给WebSecurityConfigurerAdapter做动态代理,在init方法中实现 SsoSecurityConfigurer的configure逻辑。
追加OAuth2ClientAuthenticationConfigurer到http中,最终添加OAuth2ClientAuthenticationProcessingFilter到SecurityContext imtoken的官网的下载的网址 filter中,实现oauth getAccessToken等相关逻辑。
只处理指定的请求,通过配置security.oauth2.sso.loginPath控制。
3.2、追加LoginUrlAuthenticationEntryPoint和HttpStatusEntryPoint:
ExceptionTranslationFilter中拦截AuthenticationException跳转到security.oauth2.sso.loginPath地址,进入 OAuth2ClientAuthenticationProcessingFilter拦截,实现 token逻辑。
4、ResourceServerTokenServicesConfiguration 说明:
授权服务在其他应用时生效,通过OAuth2RestTemplate去远程调用授权服务。同时提供JwtTokenServicesConfiguration相关逻辑,提供jwt token pulic key获取等相关处理。
5、OAuth2RestOperationsConfiguration说明:
默认的client配置,基于security.oauth2.client配置提供OAuth2ProtectedResourceDetails和OAuth2ClientContext默认声明。
1、通过流程可以看出来,核心在于OAuth2ClientAuthenticationProcessingFilter实现本地应用登录请求和远端认证授权服务的自动跳转。
2、通知提供jwt相关支撑。
3、本地应用判断是否登录,默认通过session控制。
1、@EnableAuthorizationServer
@Import({AuthorizationServerEndpointsConfiguration.class, AuthorizationServerSecurityConfiguration.class})
2、AuthorizationServerEndpointsConfiguration说明:
2.1、@Import(TokenKeyEndpointRegistrar.class)
TokenKeyEndpointRegistrar: 如果存在JwtAccessTokenConverter bean,就创建TokenKeyEndpoint bean。
2.2、authorizationEndpoint bean声明:用来作为请求者获得授权的服务
2.2.1、/oauth/confirm_access 用户确认授权提交端点。
2.2.2、/oauth/error 授权服务错误信息端点。
2.2.3、/oauth/authorize 请求授权端点。
2.2.4、代码块如下:
2.3、tokenEndpoint bean 声明:用来作为请求者获得令牌(Token)的服务。
2.3.1、/oauth/token 请求令牌端点
2.3.2、核心代码:
2.4 CheckTokenEndpoint bean 声明:资源服务访问的令牌解析服务。
2.4.1、/oauth/check_token:用于资源服务访问的令牌解析端点。
2.4.2、核心代码:
imtoken钱包官方下载地方在哪
2.5 WhitelabelApprovalEndpoint bean 声明: 用户确认授权提交服务
2.5.1、/oauth/confirm_access:用户确认授权提交端点。
2.5.2、核心代码:
2.6 WhitelabelErrorEndpoint bean 声明:授权服务错误信息服务
2.6.1、/oauth/error:授权服务错误信息端点。
2.6.2、核心代码:
2.7 ConsumerTokenServices ban声明:提供revokeToken支撑
2.8 AuthorizationServerTokenServices bean声明:token操作相关服务。
3、AuthorizationServerSecurityConfiguration说明
3.1、@Import({ ClientDetailsServiceConfiguration.class, AuthorizationServerEndpointsConfiguration.class })
ClientDetailsServiceConfiguration:根据configurer生成 ClientDetailsService bean,通过重写org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurer#configure(org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer)自定义实现。
AuthorizationServerEndpointsConfiguration:同2.
3.2 、继承WebSecurityConfigurerAdapter,实现认证服务请求的授权控制。
通过声明AuthorizationServerConfigurer bean实现配置修改。
3.3、核心http配置如下:
4、AuthorizationServerConfigurer提供的三个覆盖点如下:
configure(AuthorizationServerSecurityConfigurer security)
configure(ClientDetailsServiceConfigurer clients)
configure(AuthorizationServerEndpointsConfigurer endpoints)
1、通过流程可以看出来,核心在AuthorizationServerConfigurer方法的重写,提供业务支撑。
2、spring boot 集成了AuthorizationServerProperties 和 OAuth2AuthorizationServerConfiguration实现了通用的oauth2配置。OAuth2AuthorizationServerConfiguration就是AuthorizationServerConfigurer的实现。
3、/oauth/authorize请求,通过 登录相关WebSecurityConfigurerAdapter(最基本的spring security使用)处理,未登录就跳转到登陆界面,该拦截基本包含所有必要的请求。优先级在AuthorizationServerSecurity之后。
4、登录成功后,通过获取保存在session的上一步url,进行页面跳转控制。具体见SavedRequestAwareAuthenticationSuccessHandler。
5、授权服务器本身的登录保持,默认通过session来控制。
1、@EnableResourceServer
@Import(ResourceServerConfiguration.class)
2、ResourceServerConfiguration说明:
提供WebSecurityConfigurerAdapter, 追加ResourceServerSecurityConfigurer配置,主要追加OAuth2AuthenticationEntryPoint、OAuth2AccessDeniedHandler、OAuth2WebSecurityExpressionHandler等配置,控制token验证相关逻辑。
OAuth2AuthenticationProcessingFilter是核心逻辑,控制token解析相关。
3、一个filter 列表展示:
1、主要是通过多个WebSecurityConfigurerAdapter加载,实现不同的filter chain匹配。
2、资源请求,需要保证不能被前面的WebSecurityConfigurerAdapter包含,否则,无法走到token解析逻辑中。
某些场景下 单点登录系统sso 和 应用系统sso-client直接网络无法联通,可以考虑直接使用implicit方式的oauth2 + jwt 进行实现。
实现目标:
多应用系统单点登录功能(一次登录,访问多个系统,默认通过sso会话实现登录保持,集群环境可以考虑将session也转成token)。
应用系统 token 鉴权访问,一个token,可以访问所有应用系统资源。
注:authorization_code 方式,多了code换取token步骤,需要应用系统能够访问到单点登录系统。
需要注意几个角色:
授权系统:发放token的系统,就是我们的sso。
资源系统(应用系统):使用token校验权限的系统,就是我们的sso-client。
用户:页面操作人,持有用户名、密码。
五种场景
authorization_code:授权码类型,授权系统针对登录用户下发code,应用系统拿着code去授权系统换取token。
implicit:隐式授权类型。authorization_code的简化类型,授权系统针对登录系统直接下发token,302 跳转到应用系统url。
password:资源所有者(即用户)密码类型。应用系统采集到用户名密码,调用授权系统获取token。
client_credentials:客户端凭据(客户端ID以及Key)类型。没有用户参与,应用系统单纯的使用授权系统分配的凭证访问授权系统。
refresh_token:通过授权获得的刷新令牌 来获取 新的令牌。
参考: RFC规范
流程图如下:
简要说明:
Resource Owner:用户,用户名、密码持有人。
User-Agent:可以简要理解为浏览器。
Client:资源调用请求发起方,对于我们的demo来说,发起请求的浏览器就是客户端。
Authorization Server:授权系统,包含用户名密码采集和验证。
Web-Hosted Client Resource:资源系统,提供受保护的资源。
所以,对于我们来说,实际流程大体为:
其中 sso的 登录判断 和 上一步的url 默认都是通过session来实现的,如果想要集群化,可以将这两步改造成外部缓存(如 redis),或者交给客户端保持(如:jwt 生成 token 存放在 cookie )。
关键步骤说明:
sso login WebSecurityConfigurerAdapter 配置:拦截所有请求,未登录,需要登录。
sso @EnableAuthorizationServer 实现授权服务:拦截部分oauth请求,优先级在login WebSecurityConfigurerAdapter纸上。
sso 自定义UserDetailsService 处理用户校验和权限获取。
sso-client @EnableOAuth2Sso 实现拦截资源到sso登录界面。
sso-client 自定义implicit相关,完成流程。
sso-client @EnableResourceServer 实现资源访问 token解析。
Implicit自定义配置
自定义 OAuth2RestTemplate,支持 implicit
自定义 ImplicitAccessTokenProvider,没有token时候302跳到sso获取token
keytool -genkeypair -alias test -keyalg RSA -validity 365000 -keystore keystore.jks -storepass foobar -keypass foobar -keysize 1024 -v -dname “C=CN,ST=AnHui,L=HeFei,O=Wondertek,OU=prod,CN=www.*.com”
keytool -list -alias test -rfc –keystore keystore.jks -storepass foobar | openssl x509 -inform pem -pubkey
(一)、spring boot security 认证–自定义登录实现
(二)、spring boot security 授权–自定义授权实现
(三)、spring boot security 加载流程简介
(四)、spring boot security 请求流程 和 filter 说明
(五)、spring boot security SecurityProperties 配置说明
(一)、Spring Security OAuth2 五种授权方式介绍
(二)、Spring Security OAuth2 四个常用注解说明
(三)、基于Spring Security OAuth2 实现 implicit + jwt 方式的单点登录
微信扫一扫关注该公众号
JWT RS256加解密、JWK获取PublicKey和PrivateKey、从已存在公私钥加解密JWT_jwt key-CSDN博客
JWT的简介就没什么必要了, https://jwt.io/ 官网在这, 直接重点, 在alg=RS256时, 怎么从现有的JWK中获取到公?拿到公私钥后如何加密解密? 背景是在使用kong网关时, 使用jwt插件遇到的问题.
jwk在线生成
https://jwt.io/ jwt官网
http://jwt.calebb.net/ jwt反解
背景, 从其他网关切换到kong后, 有关jwt的配置需要从现有的jwk配置获取, jwk的形式如下, 可从https://mkjwk.org/ 生成, RSA tab页
jwk只是存储公私钥的一个形式, 可以从上面的key中获取到publicKey, demo如下
需添加依赖,
下载地址: http://www.chilkatsoft.com/java.asp,下载文件中有个lib库, 需要启动时指定java.library.path, Idea在run Configuration中制定vm Options, 如下:
类库也提供了从jwk中获取私钥的功能, 这时的jwk
java demo
现在就已经拿到公私钥了, 接下来可以用在kong上尝试配置一下能否加解密成功, 不想手动写代码生成Token可以用在线工具: https://jwt.io/ 选择RS256, publickey和privatekey填上刚才生成的, 如果一切正常, 左侧会出现token, 左侧下部会提示Signature Verified
然后就可以在kong上配置公钥, 配置Consumer, 具体kong的配置先不说了
kong的校验结束后, 如果我们想在java端加解密, 还需要注意密钥的填充格式的问题, 现在获取出来的密钥是pkcs1的, 如果希望用下面的方式获取PrivateKey, 在根据这个PrivateKey加密得到jwt, 那么需要转化为pkcs8填充方式
如果不转化会报错
从pkcs1转为pkcs8的命令为
之后就可以用pkcs8格式的密钥生成token, 用publicKey解密了. 全部demo如下, 在resources/jwk下放这几个文件, PublicKey是从生成JWK的网站上拷贝的 Public Key 部分的数据, PublicAndPrivateKeyPair是拷贝的Public and Keypair部分的数据, JwtBody.json如下
https://www.example-code.com/java/publickey_rsa_load_jwk.asp
https://github.com/jwtk/jjwt jwt工具
imtoken钱包下载地方是多少