oracle access manager token,AuthenticationManager验证原理
AuthenticationManager相关类图
AuthenticationManager验证过程
AuthenticationManager验证过程涉及到的类和接口较多,我们就从这里开始逐一分析,首先我手画了一张图作为索引,这张图说明了各个类和接口之间的关系。
AuthenticationManager 为认证管理接口类,其定义了认证方法authenticate()。
ProviderManager 为验证管理类,实现了接口AuthenticationManager ,并在认证方法authenticate() 中将身份认证委托给具有认证资格的AuthenticationProvider 进行身份认证。
从上图中我们可以看到AuthenticationManager的实现类有很多,至于为什么我只提及到ProviderManager,有时间的小伙伴可以进行源码跟踪就能发现。
ProviderManager的成员变量
关于AuthenticationEventPublisher不懂的小伙伴可以查看Security中的认证事件发布器 。
providers存储了一个 AuthenticationProvider 类型的list。和Security中的配置文件相对应。
MessageSourceAccessor一个国际化消息来源访问器,Security中用于信息提示。
AuthenticationProvider
接口认证类,定义了认证方法authenticate() 。
AbstractUserDetailsAuthenticationProvider 为认证抽象类,实现了接口 AuthenticationProvider 定义的认证方法 authenticate()。还定义了抽象方法 retrieveUser() 用于查询数据库用户信息,以及抽象方法 additionalAuthenticationChecks() 用作额外的身份验证检查。
DaoAuthenticationProvider
继承自抽象类AbstractUserDetailsAuthenticationProvider,实现了该类的方法 retrieveUser() 和 additionalAuthenticationChecks()。
DaoAuthenticationProvider 中还具有四个成员变量,分别是
USER_NOT_FOUND_PASSWORD
顾名思义,该变量是与PasswordEncoder一同使用的,当Security未找到用户时,用于PasswordEncoder.matches()执行的明文密码,以防止恶意用户确定用户名是否有效的旁路攻击的可能性。
PasswordEncoder
密码编码器,Security中的主要作用是用于将明文密码转换成密文,它采用SHA-256算法,迭代1024次,使用一个密钥(site-wide secret)以及8位随机盐对原密码进行加密。
userNotFoundEncodedPassword
同上方USER_NOT_FOUND_PASSWORD一致,只不过Security将其修饰为volatile的,确保了该变量不会因为编译器的优化而被省略。
关于volatile关键字不懂的请查看volatile理解
UserDetailsService
这个变量相信很多人都知道,不做过多的解释,Security中用于查询用户详细信息的接口
UserDetailsPasswordService
顾名思义,该接口用于修改用户的密码,只有在使用持久存储库时才有效,基于内存的方式会抛异常。(用处不大,只做了解即可)
流程分析
static final class AuthenticationManagerDelegator implements AuthenticationManager {
private AuthenticationManagerBuilder delegateBuilder;
private AuthenticationManager delegate;
private final Object delegateMonitor = new Object();
AuthenticationManagerDelegator(AuthenticationManagerBuilder delegateBuilder) {
Assert.notNull(delegateBuilder, "delegateBuilder cannot be null");
this.delegateBuilder = delegateBuilder;
}
@Override
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
if (this.delegate != null) {
return this.delegate.authenticate(authentication);
}
synchronized (this.delegateMonitor) {
if (this.delegate == null) {
this.delegate = this.delegateBuilder.getObject();
this.delegateBuilder = null;
}
}
return this.delegate.authenticate(authentication);
}
@Override
public String toString() {
return "AuthenticationManagerDelegator [delegate=" + this.delegate + "]";
}
}
1、Security认证的入口为AuthenticationManager的authenticate()方法,从上面代码中我们可以看出,AuthenticationManagerDelegator使用了单例模式来防止AuthenticationManager在初始化时发生无限递归,因此我们只分析上方的两个实现类OAuth2AuthenticationManager和ProviderManager。
OAuth2AuthenticationManager
OAuth2AuthenticationManager的authenticate()的方法代码如下:
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
if (authentication == null) {
throw new InvalidTokenException("Invalid token (token not found)");
}
(1) String token = (String) authentication.getPrincipal();
(2) OAuth2Authentication auth = tokenServices.loadAuthentication(token);
if (auth == null) {
throw new InvalidTokenException("Invalid token: " + token);
}
Collection resourceIds = auth.getOAuth2Request().getResourceIds();
if (resourceId != null && resourceIds != null && !resourceIds.isEmpty() && !resourceIds.contains(resourceId)) {
throw new OAuth2AccessDeniedException("Invalid token does not contain resource id (" + resourceId + ")");
}
(3) checkClientDetails(auth);
if (authentication.getDetails() instanceof OAuth2AuthenticationDetails) {
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails();
// Guard against a cached copy of the same details
if (!details.equals(auth.getDetails())) {
// Preserve the authentication details from the one loaded by token services
details.setDecodedDetails(auth.getDetails());
}
}
auth.setDetails(authentication.getDetails());
auth.setAuthenticated(true);
return auth;
}
private void checkClientDetails(OAuth2Authentication auth) {
if (clientDetailsService != null) {
ClientDetails client;
try {
client = clientDetailsService.loadClientByClientId(auth.getOAuth2Request().getClientId());
}
catch (ClientRegistrationException e) {
throw new OAuth2AccessDeniedException("Invalid token contains invalid client id");
}
Set allowed = client.getScope();
for (String scope : auth.getOAuth2Request().getScope()) {
if (!allowed.contains(scope)) {
throw new OAuth2AccessDeniedException(
"Invalid token contains disallowed scope (" + scope + ") for this client");
}
}
}
}
OAuth2AuthenticationManager用于集成了OAuth2.0时使用的,如果没有用到,可以忽略。
其中:
(1)处的代码,期望传入的身份验证请求具有一个主体值,该主体值是一个访问令牌值(一般在(authorization header)请求头中)
(2)处从ResourceServerTokenServices通过查询数据库中 oauth_client_details该表,加载身份验证。
通过(3)处检查资源id是否包含在授权请求中。检查通过之后封装OAuth2认证实体返回给UsernamePasswordAuthenticationFilter以确定认证成功或失败。
ProviderManager
ProviderManager的authenticate()的方法代码如下:
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
Class extends Authentication> toTest = authentication.getClass();
AuthenticationException lastException = null;
AuthenticationException parentException = null;
Authentication result = null;
Authentication parentResult = null;
boolean debug = logger.isDebugEnabled();
(1) for (AuthenticationProvider provider : getProviders()) {
if (!provider.supports(toTest)) {
continue;
}
if (debug) {
logger.debug("Authentication attempt using "
+ provider.getClass().getName());
}
try {
(2) result = provider.authenticate(authentication);
if (result != null) {
copyDetails(authentication, result);
break;
}
}
catch (AccountStatusException | InternalAuthenticationServiceException e) {
prepareException(e, authentication);
// SEC-546: Avoid polling additional providers if auth failure is due to
// invalid account status
throw e;
} catch (AuthenticationException e) {
lastException = e;
}
}
if (result == null && parent != null) {
// Allow the parent to try.
try {
result = parentResult = parent.authenticate(authentication);
}
catch (ProviderNotFoundException e) {
// ignore as we will throw below if no other exception occurred prior to
// calling parent and the parent
// may throw ProviderNotFound even though a provider in the child already
// handled the request
}
catch (AuthenticationException e) {
lastException = parentException = e;
}
}
if (result != null) {
if (eraseCredentialsAfterAuthentication
&& (result instanceof CredentialsContainer)) {
// Authentication is complete. Remove credentials and other secret data
// from authentication
((CredentialsContainer) result).eraseCredentials();
}
// If the parent AuthenticationManager was attempted and successful than it will publish an AuthenticationSuccessEvent
// This check prevents a duplicate AuthenticationSuccessEvent if the parent AuthenticationManager already published it
if (parentResult == null) {
eventPublisher.publishAuthenticationSuccess(result);
}
return result;
}
// Parent was null, or didn't authenticate (or throw an exception).
if (lastException == null) {
lastException = new ProviderNotFoundException(messages.getMessage(
"ProviderManager.providerNotFound",
new Object[] { toTest.getName() },
"No AuthenticationProvider found for {0}"));
}
// If the parent AuthenticationManager was attempted and failed than it will publish an AbstractAuthenticationFailureEvent
// This check prevents a duplicate AbstractAuthenticationFailureEvent if the parent AuthenticationManager already published it
if (parentException == null) {
prepareException(lastException, authentication);
}
throw lastException;
}
其中:
(1)处的代码从ProviderManager的属性providers[List]中通过for循环拿到支持该类认证的AuthenticationProvider用于认证处理。
(2)处的代码,对用户进行身份认证,认证过程如下所示。
在上面(2)处的代码,使用了AbstractUserDetailsAuthenticationProvider的authenticate()方法,接下来具体分析该方法,代码如下:
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
() -> messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.onlySupports",
"Only UsernamePasswordAuthenticationToken is supported"));
// Determine username
String username = (authentication.getPrincipal() == null) ? "NONE_PROVIDED"
: authentication.getName();
boolean cacheWasUsed = true;
UserDetails user = this.userCache.getUserFromCache(username);
if (user == null) {
cacheWasUsed = false;
try {
(1) user = retrieveUser(username,
(UsernamePasswordAuthenticationToken) authentication);
}
catch (UsernameNotFoundException notFound) {
logger.debug("User '" + username + "' not found");
if (hideUserNotFoundExceptions) {
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
else {
throw notFound;
}
}
Assert.notNull(user,
"retrieveUser returned null - a violation of the interface contract");
}
try {
preAuthenticationChecks.check(user);
(2) additionalAuthenticationChecks(user,
(UsernamePasswordAuthenticationToken) authentication);
}
catch (AuthenticationException exception) {
if (cacheWasUsed) {
// There was a problem, so try again after checking
// we're using latest data (i.e. not from the cache)
cacheWasUsed = false;
user = retrieveUser(username,
(UsernamePasswordAuthenticationToken) authentication);
preAuthenticationChecks.check(user);
additionalAuthenticationChecks(user,
(UsernamePasswordAuthenticationToken) authentication);
}
else {
throw exception;
}
}
postAuthenticationChecks.check(user);
if (!cacheWasUsed) {
this.userCache.putUserInCache(user);
}
Object principalToReturn = user;
if (forcePrincipalAsString) {
principalToReturn = user.getUsername();
}
(3) return createSuccessAuthentication(principalToReturn, authentication, user);
}
(3)处创建一个成功的身份认证令牌并将用户认证信息其放置到UsernamePasswordAuthenticationToken中。
查看源码我们得知,AbstractUserDetailsAuthenticationProvider的(1)处和(2)处调用的方法没有具体的实现,因此我们接下来分析它的子类DaoAuthenticationProvider
retrieveUser()
protected final UserDetails retrieveUser(String username,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
prepareTimingAttackProtection();
try {
(1) UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
if (loadedUser == null) {
throw new InternalAuthenticationServiceException(
"UserDetailsService returned null, which is an interface contract violation");
}
return loadedUser;
}
catch (UsernameNotFoundException ex) {
mitigateAgainstTimingAttack(authentication);
throw ex;
}
catch (InternalAuthenticationServiceException ex) {
throw ex;
}
catch (Exception ex) {
throw new InternalAuthenticationServiceException(ex.getMessage(), ex);
}
}
(1)处调用DaoAuthenticationProvider成员变量UserDetailsService的方法loadUserByUsername()从数据库中加载用户详细信息(用过Security的对此处应该是很熟悉了)
additionalAuthenticationChecks()
protected void additionalAuthenticationChecks(UserDetails userDetails,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
if (authentication.getCredentials() == null) {
logger.debug("Authentication failed: no credentials provided");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
(1) String presentedPassword = authentication.getCredentials().toString();
(2) if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
logger.debug("Authentication failed: password does not match stored value");
throw new BadCredentialsException(messages.getMessage(
"AbstractUserDetailsAuthenticationProvider.badCredentials",
"Bad credentials"));
}
}
(1)处从UsernamePasswordAuthenticationToken中调出了密码,再由(2)处通过调用成员变量passwordEncoder对其密码进行验证。
以上就是AuthenticationManager的验证大致流程,由于本人能力有限,如有错误,还请各位大佬多多包涵并在评论区进行留言指正,我会一一回复。
oracle access manager token,AuthenticationManager验证原理相关推荐
- oracle access manager token,Laravel 自带的 API 守卫驱动 token 使用详解
在Laravel框架中,默认的用户认证守卫有两个,web和api,web守卫默认的驱动是session,而api守卫默认的驱动是token.那么,该如何使用这个token驱动? 寻找 TokenGua ...
- AuthenticationManager验证原理分析
本文来说下AuthenticationManager验证原理 文章目录 AuthenticationManager概述 AuthenticationManager相关类图 security认证流程 A ...
- spring boot 集成 Oracle Access Manager(OAM)单点登录
1.介绍 Oracle Access Manager(OAM)是oracle公司开发的身份认证和资源管理解决方案.结合WebGate和OHS可实现系统间单点登录集成. oracle中间件产品可以非常方 ...
- oracle不能访问管理页面,无法登录Oracle Access Manager的OAM控制台
登录后我无法访问OAMConsole主页 . OAM安装在Centos 6.4 Server Edition上,我通过FireFox访问控制台 . 我可以毫无问题地访问控制台和企业管理器 . 但是,当 ...
- 【转】基于Token的身份验证原理
目录 1 发展史 2 Cookie 3 Session 3.1 cookie和session的区别 4 Token 4.1 传统方式--基于服务器的验证 4.2 基于服务器验证方式暴露的一些问题 4. ...
- shopnc 支持 支付宝快捷登陆 shopnc权限验证原理说明
为目前使用的是shopnc商场二次开发,shopnc本身做了qq互联和微博快捷登陆的api,做成了集成通用的接口 首先说下基本的这种类型的api访问方式,首先,的有个配置文件,配置你申请的id和key ...
- FLASK RESTFUL TOKEN用户验证笔记
笔记1 REST要求无状态,可以理解为没有session,而且session的存储遇到分布式集群的情况就比较难搞,所以对于用户验证目前网上大多数做法是token方式,第一次登录的时候,先提交用户名密码 ...
- Oracle Enterprise Manager Cloud Control 13c部署文档
一.软件准备 名称 版本号 软件包 OS 7.6 CentOS Linux release 7.6.1810 (Core) Oracle Database 19c 19.3 LINUX.X64_193 ...
- ibm tivoli_IBM Tivoli Access Manager:WebSEAL Kerberos连接
ibm tivoli 总览 IBM®Tivoli®Access Manager(TAM)v6.1引入了WebSEAL的Kerberos联结的概念. Kerberos联结可用于对已启用Kerberos身 ...
最新文章
- Nature Commun. | 假单胞和芽孢杆菌互作共存于寄主的机制,为生物防治提供新思路...
- matlab jdbc mysql_Matlab连接MySQL(ODBC及JDBC驱动)
- 小坑记录:get_cmap参数区分大小写
- SQLAlchemy之SQL Expression
- c/c++函数指针(Hook前奏1)
- 4.2.4 磁盘的管理
- 腾讯数平精准推荐 | 横扫ICDAR 2019,斩获七项冠军
- 苍溪师范94级计算机与文秘专业就业前景,文秘专业就业前景
- Java标签移动_如何使用基于鼠标单击的标签移动特定游戏对象?
- android初步ui线程案例,android – 它是一个bug还是一个功能?在某些情况下,可以从未在UI线程上运行的任务访问UI线程...
- 2018.3.10 模拟赛——(2)给出字符串
- 可拖动的进度条_视频号全新升级!进度条可拖动、支持@、支持转发......
- CSDN如何获取积分
- 什么是移动端,它和pc端有什么区别
- VB Dim的初识与相关用法
- 2019超导量子计算机最新报道,在高温超导体加持下,量子效应的量子计算机,或将迎来重大突破...
- 解决csv文件的换行问题
- 对timedelta64的理解
- 计算机监听的端口,侦听计算机的指定端口并分析其数据包,监听,本,解析
- NOIP2016 略解
热门文章
- Elasticsearch索引分析
- 本地操作HDFS报错:java.net.ConnectException: Call From KevinWong/192.168.16.1 to hadoop000:9000
- Flink分布式standalone部署方式(第二种方式)
- shutil模块,为什么说它是os模块的兄弟模块?
- 肝!一款基于 Python 语言的 Linux 资源监视器!
- 你们需要的数据集,都给准备好了!
- 13 个 Python 新手练级项目
- 深度学习入门笔记系列(三)——感知器模型和 tensorboard 的使用方法
- mysql ---- innodb-4-备份和恢复
- Flask 系列之 Blueprint