Spring Security登录的简单解析

发布时间:2023年12月21日

1.自己写的登录
    public TokenVo login(String username, String password) {

        //封装 Authentication
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
        //认证用户
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);
        if (authenticate == null) {
            throw new RuntimeException("认证失败!");
        }
        //获取认证用户信息
        UserDetailsInfo userDetailsInfo = (UserDetailsInfo) authenticate.getPrincipal();
        //认证通过,生成jwt
        TokenVo tokenVo=new TokenVo();
        String token = jwtTokenUtil.generateToken(userDetailsInfo);
        tokenVo.setToken(token);
        tokenVo.setExpireTime(jwtTokenUtil.expiration);
        redisCache.setCacheObject(token, userDetailsInfo, jwtTokenUtil.expiration, TimeUnit.SECONDS);
        return tokenVo;
    }
2.进入authenticationManager.authenticate(authenticationToken);

其所属类是ProviderManager implements AuthenticationManage

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()) {
        // 判断当前验证提供者是否支持传入的Authentication对象的类型
        if (!provider.supports(toTest)) {
            continue;
        }
        
        // 打印正在使用的验证提供者和当前位置
        logger.trace(LogMessage.format("Authenticating request with %s (%d/%d)",
              provider.getClass().getSimpleName(), ++currentPosition, size));
        
        try {
            // 调用当前验证提供者的authenticate方法进行身份验证
            result = provider.authenticate(authentication);
            
            // 如果验证成功,复制验证结果的详细信息到原始的Authentication对象中
            if (result != null) {
                //copyDetails(authentication, result);
                break;
            }
        } catch (AccountStatusException | InternalAuthenticationServiceException ex) {
            // 准备抛出异常并中止验证过程
            prepareException(ex, authentication);
            throw ex;
        } catch (AuthenticationException ex) {
            lastException = ex;
        }
    }
    
    // 如果当前验证提供者未能验证成功,并且存在父验证管理器,则尝试使用父验证管理器进行验证
    if (result == null && this.parent != null) {
        try {
            parentResult = this.parent.authenticate(authentication);
            result = parentResult;
        } catch (ProviderNotFoundException ex) {
            // 忽略该异常
        } catch (AuthenticationException ex) {
            parentException = ex;
            lastException = ex;
        }
    }
    
    // 如果验证成功,执行一些后续操作,如擦除验证结果中的敏感信息,并发布身份验证成功的事件
    if (result != null) {
        //((CredentialsContainer) result).eraseCredentials();
        //if (parentResult == null) {
        //    this.eventPublisher.publishAuthenticationSuccess(result);
        //}
        return result;
    }
    
    // 如果最后一个异常为空,则创建一个ProviderNotFoundException异常
    if (lastException == null) {
        lastException = new ProviderNotFoundException(this.messages.getMessage("ProviderManager.providerNotFound",
             new Object[] { toTest.getName() }, "No AuthenticationProvider found for {0}"));
    }
    
    // 如果父验证管理器的验证异常为空,则准备抛出最后一个异常
   if (parentException == null) {
       prepareException(lastException, authentication);
   }
    
    // 抛出最后一个异常
    throw lastException;
}

3 进入result = provider.authenticate(authentication);

其所属类public abstract class AbstractUserDetailsAuthenticationProvider implements AuthenticationProvider,
执行

// 确保传入的Authentication对象是UsernamePasswordAuthenticationToken的实例
Assert.isInstanceOf(UsernamePasswordAuthenticationToken.class, authentication,
		() -> this.messages.getMessage("AbstractUserDetailsAuthenticationProvider.onlySupports",
				"Only UsernamePasswordAuthenticationToken is supported"));

// 获取用户名
String username = determineUsername(authentication);

boolean cacheWasUsed = true;
// 从缓存中获取用户详细信息
UserDetails user = this.userCache.getUserFromCache(username);

if (user == null) {
	cacheWasUsed = false;
	try {
		// 从数据源中检索用户详细信息
		user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
	} catch (UsernameNotFoundException ex) {
		this.logger.debug("Failed to find user '" + username + "'");
		if (!this.hideUserNotFoundExceptions) {
			throw ex;
		}
		// 如果用户名未找到,抛出BadCredentialsException异常
		throw new BadCredentialsException(this.messages
				.getMessage("AbstractUserDetailsAuthenticationProvider.badCredentials", "Bad credentials"));
	}
	// 确保retrieveUser方法的实现没有返回null
	Assert.notNull(user, "retrieveUser returned null - a violation of the interface contract");
}

try {
	// 对用户进行预身份验证检查
	this.preAuthenticationChecks.check(user);
	// 对用户进行额外的身份验证检查
	additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
} catch (AuthenticationException ex) {
	if (!cacheWasUsed) {
		throw ex;
	}
	// 如果出现问题,尝试再次进行身份验证检查,确保使用最新的数据(即不使用缓存)
	cacheWasUsed = false;
	user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);
	this.preAuthenticationChecks.check(user);
	additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken) authentication);
}

// 对用户进行后身份验证检查
this.postAuthenticationChecks.check(user);

if (!cacheWasUsed) {
	// 将用户详细信息放入缓存中
	this.userCache.putUserInCache(user);
}

Object principalToReturn = user;
if (this.forcePrincipalAsString) {
	// 如果设置了forcePrincipalAsString为true,则返回用户名作为principal
	principalToReturn = user.getUsername();
}

// 创建身份验证成功的Authentication对象
return createSuccessAuthentication(principalToReturn, authentication, user);

4 进入 user = retrieveUser(username, (UsernamePasswordAuthenticationToken) authentication);

其所属类class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider
执行

/**
 * 从用户详细信息服务中检索用户详细信息。
 *
 * @param username      用户名
 * @param authentication 用于身份验证的`UsernamePasswordAuthenticationToken`对象
 * @return 用户详细信息
 * @throws AuthenticationException 如果发生身份验证异常
 */
protected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)
		throws AuthenticationException {
	// 准备防止时序攻击的措施
	prepareTimingAttackProtection();
	try {
		// 通过用户详细信息服务加载指定用户名的用户详细信息
		UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
		if (loadedUser == null) {
			// 如果加载的用户详细信息为null,抛出InternalAuthenticationServiceException异常,违反了接口约定
			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) {
		// 抛出InternalAuthenticationServiceException异常,将原始异常信息作为其原因
		throw new InternalAuthenticationServiceException(ex.getMessage(), ex);
	}
}

5 进入 UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);

调用自己写的loadUserByUsername();

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    @Transactional
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        UserDetailsInfo userDetailsInfo = sysUserMapper.getUserInfoByUsername(username);
        if(userDetailsInfo == null){
            throw  new UsernameNotFoundException("用戶不存在");
        }
        userDetailsInfo.setPassword(passwordEncoder.encode(userDetailsInfo.getPassword()));
        return userDetailsInfo;
    }
注:

用户名校验在这里插入图片描述
密码校验
在这里插入图片描述

文章来源:https://blog.csdn.net/qq_46058550/article/details/135107015
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。