最后
针对最近很多人都在面试,我这边也整理了相当多的面试专题资料,也有其他大厂的面经。希望可以帮助到大家。
上述的面试题答案都整理成文档笔记。 也还整理了一些面试资料&最新2021收集的一些大厂的面试真题(都整理成文档,小部分截图)
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
总结一下:SecurityContextPersistenceFilter它的作用就是请求来的时候将包含了认证授权信息的SecurityContext对象从SecurityContextRepository中取出交给SecurityContextHolder工具类,方便我们通过SecurityContextHolder获取SecurityContext从而获取到认证授权信息,请求走的时候又把SecurityContextHolder清空,源码如下:
public class SecurityContextPersistenceFilter extends GenericFilterBean {
…省略…
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
…省略部分代码…
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request,
response);
//从SecurityContextRepository获取到SecurityContext
SecurityContext contextBeforeChainExecution = repo.loadContext(holder);
try {
//把 securityContext设置到SecurityContextHolder,如果没认证通过,这个SecurtyContext就是空的
SecurityContextHolder.setContext(contextBeforeChainExecution);
//调用后面的filter,比如掉用usernamepasswordAuthenticationFilter实现认证
chain.doFilter(holder.getRequest(), holder.getResponse());
}
finally {
//如果认证通过了,这里可以从SecurityContextHolder.getContext();中获取到SecurityContext
SecurityContext contextAfterChainExecution = SecurityContextHolder
.getContext();
// Crucial removal of SecurityContextHolder contents - do this before anything
// else.
//删除SecurityContextHolder中的SecurityContext
SecurityContextHolder.clearContext();
//把SecurityContext 存储到SecurityContextRepository
repo.saveContext(contextAfterChainExecution, holder.getRequest(),
holder.getResponse());
request.removeAttribute(FILTER_APPLIED);
if (debug) {
logger.debug(“SecurityContextHolder now cleared, as request processing completed”);
}
}
…省略…
UsernamePasswordAuthenticationFilter
它的重用是,拦截“/login”登录请求,处理表单提交的登录认证,将请求中的认证信息包括username,password等封装成UsernamePasswordAuthenticationToken,然后调用
AuthenticationManager的认证方法进行认证。
public class UsernamePasswordAuthenticationFilter extends
AbstractAuthenticationProcessingFilter {
// ~ Static fields/initializers
// =====================================================================================
//从登录请求中获取参数:username,password的名字
public static final String SPRING_SECURITY_FORM_USERNAME_KEY = “username”;
public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = “password”;
private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
//默认支持POST登录
private boolean postOnly = true;
//默认拦截/login请求,Post方式
public UsernamePasswordAuthenticationFilter() {
super(new AntPathRequestMatcher(“/login”, “POST”));
}
// ~ Methods
// ========================================================================================================
public Authentication attemptAuthentication(HttpServletRequest request,
HttpServletResponse response) throws AuthenticationException {
//判断请求是否是POST
if (postOnly && !request.getMethod().equals(“POST”)) {
throw new AuthenticationServiceException(
"Authentication method not supported: " + request.getMethod());
}
//获取到用户名和密码
String username = obtainUsername(request);
String password = obtainPassword(request);
if (username == null) {
username = “”;
}
if (password == null) {
password = “”;
}
username = username.trim();
//用户名和密码封装Token
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(
username, password);
//设置details属性
// Allow subclasses to set the “details” property
setDetails(request, authRequest);
//调用AuthenticationManager().authenticate进行认证,参数就是Token对象
return this.getAuthenticationManager().authenticate(authRequest);
}
AuthenticationManager
请求通过UsernamePasswordAuthenticationFilter调用AuthenticationManager,默认走的实现类是ProviderManager,它会找到能支持当前认证的AuthenticationProvider实现类调用器authenticate方法执行认证,认证成功后会清除密码,然后抛出AuthenticationSuccessEvent事件
public class ProviderManager implements AuthenticationManager, MessageSourceAware,
InitializingBean {
…省略…
//这里authentication 是封装了登录请求的认证参数,
//即:UsernamePasswordAuthenticationFilter传入的Token对象
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();
//找到所有的AuthenticationProvider ,选择合适的进行认证
for (AuthenticationProvider provider : getProviders()) {
//是否支持当前认证
if (!provider.supports(toTest)) {
continue;
}
if (debug) {
logger.debug("Authentication attempt using "
- provider.getClass().getName());
}
try {
//调用provider执行认证
result = provider.authenticate(authentication);
if (result != null) {
copyDetails(authentication, result);
break;
}
}
…省略…
}
…省略…
//result就是Authentication ,使用的实现类依然是UsernamepasswordAuthenticationToken,
//封装了认证成功后的用户的认证信息和授权信息
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;
}
DaoAuthenticationProvider
请求到达AuthenticationProvider,默认实现是DaoAuthenticationProvider,它的作用是根据传入的Token中的username调用UserDetailService加载数据库中的认证授权信息(UserDetails),然后使用PasswordEncoder对比用户登录密码是否正确
public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
//密码编码器
private PasswordEncoder passwordEncoder;
//UserDetailsService ,根据用户名加载UserDetails对象,从数据库加载的认证授权信息
private UserDetailsService userDetailsService;
//认证检查方法
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”));
}
//获取密码
String presentedPassword = authentication.getCredentials().toString();
//通过passwordEncoder比较密码,presentedPassword是用户传入的密码,userDetails.getPassword()是从数据库加载到的密码
//passwordEncoder编码器不一样比较密码的方式也不一样
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”));
}
}
//检索用户,参数为用户名和Token对象
protected final UserDetails retrieveUser(String username,
UsernamePasswordAuthenticationToken authentication)
throws AuthenticationException {
prepareTimingAttackProtection();
try {
//调用UserDetailsService的loadUserByUsername方法,
//根据用户名检索数据库中的用户,封装成UserDetails
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);
}
}
//创建认证成功的认证对象Authentication,使用的实现是UsernamepasswordAuthenticationToken,
//封装了认证成功后的认证信息和授权信息,以及账户的状态等
@Override
protected Authentication createSuccessAuthentication(Object principal,
Authentication authentication, UserDetails user) {
boolean upgradeEncoding = this.userDetailsPasswordService != null
&& this.passwordEncoder.upgradeEncoding(user.getPassword());
if (upgradeEncoding) {
String presentedPassword = authentication.getCredentials().toString();
String newPassword = this.passwordEncoder.encode(presentedPassword);
user = this.userDetailsPasswordService.updatePassword(user, newPassword);
}
return super.createSuccessAuthentication(principal, authentication, user);
}
…省略…
这里提供了三个方法
-
additionalAuthenticationChecks:通过passwordEncoder比对密码
-
retrieveUser:根据用户名调用UserDetailsService加载用户认证授权信息
-
createSuccessAuthentication:登录成功,创建认证对象Authentication
分享
首先分享一份学习大纲,内容较多,涵盖了互联网行业所有的流行以及核心技术,以截图形式分享:
(亿级流量性能调优实战+一线大厂分布式实战+架构师筑基必备技能+设计思想开源框架解读+性能直线提升架构技术+高效存储让项目性能起飞+分布式扩展到微服务架构…实在是太多了)
其次分享一些技术知识,以截图形式分享一部分:
Tomcat架构解析:
算法训练+高分宝典:
Spring Cloud+Docker微服务实战:
最后分享一波面试资料:
切莫死记硬背,小心面试官直接让你出门右拐
1000道互联网Java面试题:
Java高级架构面试知识整理:
(img-UImRslGf-1715235561458)]
算法训练+高分宝典:
[外链图片转存中…(img-t58dTLXE-1715235561458)]
Spring Cloud+Docker微服务实战:
[外链图片转存中…(img-cQjNK9iU-1715235561459)]
最后分享一波面试资料:
切莫死记硬背,小心面试官直接让你出门右拐
1000道互联网Java面试题:
[外链图片转存中…(img-3tDvSleq-1715235561459)]
Java高级架构面试知识整理:
[外链图片转存中…(img-h4nIuCiR-1715235561459)]