最后
码字不易,觉得有帮助的可以帮忙点个赞,让更多有需要的人看到
又是一年求职季,在这里,我为各位准备了一套Java程序员精选高频面试笔试真题,来帮助大家攻下BAT的offer,题目范围从初级的Java基础到高级的分布式架构等等一系列的面试题和答案,用于给大家作为参考
以下是部分内容截图
}
public Filter getFilter() {
return this.filter;
}
public static class User {
private String name = "user";
private String password = UUID.randomUUID().toString();
private List<String> roles = new ArrayList();
private boolean passwordGenerated = true;
public User() {
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
if (StringUtils.hasLength(password)) {
this.passwordGenerated = false;
this.password = password;
}
}
public List<String> getRoles() {
return this.roles;
}
public void setRoles(List<String> roles) {
this.roles = new ArrayList(roles);
}
public boolean isPasswordGenerated() {
return this.passwordGenerated;
}
}
public static class Filter {
private int order = -100;
private Set<DispatcherType> dispatcherTypes;
public Filter() {
this.dispatcherTypes = new HashSet(Arrays.asList(DispatcherType.ASYNC, DispatcherType.ERROR, DispatcherType.REQUEST));
}
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
public Set<DispatcherType> getDispatcherTypes() {
return this.dispatcherTypes;
}
public void setDispatcherTypes(Set<DispatcherType> dispatcherTypes) {
this.dispatcherTypes = dispatcherTypes;
}
}
}
@ConfigurationProperties这个注解作用就是将SpringBoot配置文件的内容和当前的属性做绑定,并且在绑定的时候,会去SpringBoot的配置文件中,会找到这个前缀为spring.security这样的配置,很明显在我们当前的springboot程序中,我们并没有这这些事。像这种情况下,这里边的配置属性都会使用默认值。这个SecurityProperties类中有一个User属性,这个就对应了一个默认的User对象。User是SecurityProperties的一个静态内部类。
在我们在SpringBoot项目中的配置文件没有对SpringSecurity做任何配置情况下,这个User的name就是“user”,password就是一个uuid。
public static class User {
private String name = “user”;
private String password = UUID.randomUUID().toString();
private List roles = new ArrayList();
private boolean passwordGenerated = true;
public User() {
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return this.password;
}
public void setPassword(String password) {
if (StringUtils.hasLength(password)) {
this.passwordGenerated = false;
this.password = password;
}
}
public List<String> getRoles() {
return this.roles;
}
public void setRoles(List<String> roles) {
this.roles = new ArrayList(roles);
}
public boolean isPasswordGenerated() {
return this.passwordGenerated;
}
}
到这里,就解释了控制台中的账号和密码是怎么生成的了。
private String name = "user";
private String password = UUID.randomUUID().toString();
### 2:从表单认证流程角度再来追踪源代码
现在我们回到SecurityAutoConfiguration这个类,这个是引入jar包之后,Springboot就会自动加载的那个SpringSecurity的核心配置文件。这个类的上边还有一个注解:
@Import({SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class, SecurityDataConfiguration.class})
@Configuration
@ConditionalOnClass({DefaultAuthenticationEventPublisher.class})
@EnableConfigurationProperties({SecurityProperties.class})
@Import({SpringBootWebSecurityConfiguration.class, WebSecurityEnablerConfiguration.class, SecurityDataConfiguration.class})
public class SecurityAutoConfiguration {
public SecurityAutoConfiguration() {
}
@Bean
@ConditionalOnMissingBean({AuthenticationEventPublisher.class})
public DefaultAuthenticationEventPublisher authenticationEventPublisher(ApplicationEventPublisher publisher) {
return new DefaultAuthenticationEventPublisher(publisher);
}
}
这个import注解会自动把上边这俩类加载进来。在第一个配置类当中,里边配置了SpringSecurity的默认认证方式。
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
class SpringBootWebSecurityConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnDefaultWebSecurity
static class SecurityFilterChainConfiguration {
@Bean
@Order(SecurityProperties.BASIC_AUTH_ORDER)
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests().anyRequest().authenticated();
http.formLogin();
http.httpBasic();
return http.build();
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(name = BeanIds.SPRING_SECURITY_FILTER_CHAIN)
@ConditionalOnClass(EnableWebSecurity.class)
@EnableWebSecurity
static class WebSecurityEnablerConfiguration {
}
}
@ConditionalOnWebApplication(type = Type.SERVLET)这个告诉我们,生效于Servlet类型的应用。
@Bean
@Order(SecurityProperties.BASIC_AUTH_ORDER)
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests().anyRequest().authenticated();
http.formLogin();
http.httpBasic();
return http.build();
}
这个定义了所有的http请求,都会进行认证。
这个定义了所有的http请求,都支持表单认证。
这个定义了所有的http请求,都会支持basic认证。
我们现在说的这个事是表单认证,所以我们现在进来formLogin这种表单认证方式。
public FormLoginConfigurer<HttpSecurity> formLogin() throws Exception {
return getOrApply(new FormLoginConfigurer<>());
}
public FormLoginConfigurer() {
super(new UsernamePasswordAuthenticationFilter(), null);
usernameParameter("username");
passwordParameter("password");
}
UsernamePasswordAuthenticationFilter这个类作用就是根据用户名和密码做认证,这一个默认加载的过滤器。
private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/login", "POST");
public UsernamePasswordAuthenticationFilter() {
super(DEFAULT_ANT_PATH_REQUEST_MATCHER);
}
public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter
这个时候就意味着这个过滤器被加载了,然后过滤器发挥作用的时候用的都是doFilter方法,然而这个Filter并没有doFilter方法,答案就是在他的父类当中,父类是一个抽象类,我们找一下:
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);
}
在这个公共的doFilter方法中,调用了私有的方法:
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
//不需要认证,直接结束即可
if (!requiresAuthentication(request, response)) {
chain.doFilter(request, response);
return;
}
//需要认证走接下来逻辑。
try {
Authentication authenticationResult = attemptAuthentication(request, response);
if (authenticationResult == null) {
// return immediately as subclass has indicated that it hasn't completed
return;
}
this.sessionStrategy.onAuthentication(authenticationResult, request, response);
// Authentication success
if (this.continueChainBeforeSuccessfulAuthentication) {
chain.doFilter(request, response);
}
successfulAuthentication(request, response, chain, authenticationResult);
}
catch (InternalAuthenticationServiceException failed) {
this.logger.error("An internal error occurred while trying to authenticate the user.", failed);
unsuccessfulAuthentication(request, response, failed);
}
catch (AuthenticationException ex) {
// Authentication failed
unsuccessfulAuthentication(request, response, ex);
}
}
在哪里做的认证呢?显然是在这个方法里边:
Authentication authenticationResult = attemptAuthentication(request, response);
我们查看一下认证过程:
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
//如果认证方式只支持post,又不是post。
if (this.postOnly && !request.getMethod().equals("POST")) {
throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
}
//接下来一定是post提交的表单模式。
String username = obtainUsername(request);
//获取username和password
username = (username != null) ? username.trim() : "";
String password = obtainPassword(request);
password = (password != null) ? password : "";
//把获取到的账号密码封装成一个Token对象。
UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username,
password);
// Allow subclasses to set the "details" property
setDetails(request, authRequest);
//拿着这个Token对象在这里边做认证。
return this.getAuthenticationManager().authenticate(authRequest);
}
我们查看一下详细认证过程:
@Override
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;
int currentPosition = 0;
int size = this.providers.size();
for (AuthenticationProvider provider : getProviders()) {
if (!provider.supports(toTest)) {
continue;
}
if (logger.isTraceEnabled()) {
logger.trace(LogMessage.format("Authenticating request with %s (%d/%d)",
provider.getClass().getSimpleName(), ++currentPosition, size));
}
try {
result = provider.authenticate(authentication);
if (result != null) {
copyDetails(authentication, result);
break;
}
}
catch (AccountStatusException | InternalAuthenticationServiceException ex) {
prepareException(ex, authentication);
// SEC-546: Avoid polling additional providers if auth failure is due to
// invalid account status
throw ex;
}
catch (AuthenticationException ex) {
lastException = ex;
}
}
if (result == null && this.parent != null) {
// Allow the parent to try.
try {
parentResult = this.parent.authenticate(authentication);
result = parentResult;
}
catch (ProviderNotFoundException ex) {
// 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
最后
本人也收藏了一份Java面试核心知识点来应付面试,借着这次机会可以送给我的读者朋友们:
目录:
Java面试核心知识点
一共有30个专题,足够读者朋友们应付面试啦,也节省朋友们去到处搜刮资料自己整理的时间!
Java面试核心知识点
知识点来应付面试,借着这次机会可以送给我的读者朋友们:
目录:
[外链图片转存中…(img-dzAVZUnm-1715470381395)]
Java面试核心知识点
一共有30个专题,足够读者朋友们应付面试啦,也节省朋友们去到处搜刮资料自己整理的时间!
[外链图片转存中…(img-6GEwAfmT-1715470381396)]
Java面试核心知识点