Spring Security登陆流程讲解

网友投稿 1084 2022-11-25

Spring Security登陆流程讲解

Spring Security登陆流程讲解

在Spring Security中,认证授权都是通过过滤器来实现的。

当开始登陆的时候,有一个关键的过滤器UsernamePasswordAuthenticationFilter,该类继承抽象类AbstractAuthenticationProcessingFilter,在AbstractAuthenticationProcessingFilter里有一个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);

}

}

首先requiresAuthentication先判断是否尝试校验,通过后调用attemptAuthentication方法,这个方法也就是UsernamePasswordAuthenticationFilter 中的attemptAuthentication方法。

public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)

throws AuthenticationException {

if (this.postOnly && !request.getMethod().equals("POST")) {

throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());

}

String username = obtainUsername(request);

username = (username != null) ? username : "";

username = username.trim();

String password = obtainPassword(request);

password = (password != null) ? password : "";

UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);

// Allow subclasses to set the "details" property

setDetails(request, authRequest);

return this.getAuthenticationManager().authenticate(authRequest);

}

1.在UsernamePasswordAuthenticationFilter 的attemptAuthentication方法中,先是验证请求的类型,是否是POST请求,如果不是的话,抛出异常。(PS:登陆肯定要用POST方法了)

2.然后拿到username和password。这里使用的是obtainUsername方法,也就是get方法。

@Nullable

protected String obtainPassword(HttpServletRequest request) {

return request.getParameter(this.passwordParameter);

}

@Nullable

protected String obtainUsername(HttpServletRequest request) {

return request.getParameter(this.usernameParameter);

}

由此我们知道了Spring Security中是通过get方法来拿到参数,所以在进行前后端分离的时候是无法接受jsON数据,处理方法就是自定义一个Filter来继承UsernamePasswordAuthenticationFilter,重写attemptAuthentication方法,然后创建一个Filter实例写好登陆成功和失败的逻辑处理,在HttpSecurity参数的configure中通过addFilterAt来替换Spring Security官方提供的过滤器。

3.创建一个UsernamePasswordAuthenticationToken 实例。

4.设置Details,在这里关键的是在WebAuthenticationDetails类中记录了用户的remoteAddress和sessionIzgDcEgmPdId。

public WebAuthenticationDetails(HttpServletRequest request) {

this.remoteAddress = request.getRemoteAddr();

HttpSession session = request.getSession(false);

this.sessionId = (session != null) ? session.getId() : null;

}

5.拿到一个AuthenticationManager通过authenticate方法进行校验,这里以实现类ProviderManager为例。

@Override

public Authentication authenticate(Authentication authentication) throws AuthenticationException {

//获取Authentication的运行时类

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()) {

//判断是否支持处理该类别的provider

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

// handled the request

zgDcEgmPdI}

catch (AuthenticationException ex) {

parentException = ex;

lastException = ex;

}

}

if (result != null) {

//擦除用户的凭证 也就是密码

if (this.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 then it

// will publish an AuthenticationSuccessEvent

// This check prevents a duplicate AuthenticationSuccessEvent if the parent

// AuthenticationManager already published it

if (parentResult == null) {

//公示登陆成功

this.eventPublisher.publishAuthenticationSuccess(result);

}

return result;

}

// Parent was null, or didn't authenticate (or throw an exception).

if (lastException == null) {

lastException = new ProviderNotFoundException(this.messages.getMessage("ProviderManager.providerNotFound",

new Object[] { toTest.getName() }, "No AuthenticationProvider found for {0}"));

}

// If the parent AuthenticationManager was attempted and failed then 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;

}

6.经过一系列校验,此时登陆校验基本完成,当验证通过后会执行doFilter中的successfulAuthentication方法,跳转到我们设置的登陆成功界面,验证失败会执行unsuccessfulAuthentication方法,跳转到我们设置的登陆失败界面。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:ChAMP 分析甲基化芯片数据-归一化篇
下一篇:ChAMP分析甲基化芯片数据-差异分析上篇
相关文章

 发表评论

暂时没有评论,来抢沙发吧~