1、前端或小程序调用/login 来调用接口。
/**
* 登录验证
*
* @param username 用户名
* @param password 密码
* @return 结果
*/
public String login(String username, String password) {
// 用户验证
Authentication authentication;
try {
authentication = login(new DecorationAuthenticationToken(username, password));
decorationDetailsService.loadUserByUsername(username);
} catch (Exception e) {
if (e instanceof BadCredentialsException) {
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
throw new UserPasswordNotMatchException();
} else {
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
throw new CustomException(e.getMessage());
}
}
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
LoginUser decorationUser = (LoginUser) authentication.getPrincipal();
decorationUser.getDecoration().setPassword(password);
tokenService.setLoginUser(decorationUser);
// 生成token
return tokenService.createToken(decorationUser);
}
/**
* 校验账号密码并进行登陆
*
* @param upToken
*/
private Authentication login(UsernamePasswordAuthenticationToken upToken) {
//验证
Authentication authentication = authenticationManager.authenticate(upToken);
//将用户信息保存到SecurityContextHolder=登陆
SecurityContextHolder.getContext().setAuthentication(authentication);
return authentication;
}
2、首先我这个登录是不需要验证码的,只需要username 和 password都对应得上就可,而且我的password是通过
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
passwordEncoder.encode(password)
进行加密的
3、登录进来是需要对username和password进行验证
Authentication authentication = authenticationManager.authenticate(upToken);
这个方法会找到authenticationManager(认证管理器)的实现类 ProviderManager
4、ProviderManager下的authenticate.(upToken)方法下的
result = provider.authenticate(authentication);
provider是List 是认证提供者
AuthenticationProvider:解释一下认证提供者
认证是由 AuthenticationManager 来管理的,但是真正进行认证的是 AuthenticationManager 中定义的 AuthenticationProvider。AuthenticationManager 中可以定义有多个 AuthenticationProvider。当我们使用 authentication-provider 元素来定义一个 AuthenticationProvider 时,如果没有指定对应关联的 AuthenticationProvider 对象,Spring Security 默认会使用 DaoAuthenticationProvider。DaoAuthenticationProvider 在进行认证的时候需要一个 UserDetailsService 来获取用户的信息 UserDetails,其中包括用户名、密码和所拥有的权限等。所以如果我们需要改变认证的方式,我们可以实现自己的 AuthenticationProvider;如果需要改变认证的用户信息来源,我们可以实现 UserDetailsService。
————————————————
版权声明:本文为CSDN博主「lifeifei2010」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lifeifei2010/article/details/78787558
5、AuthenticationProvider下的认证方法 authenticate()
这时会去调用AbstractUserDetailsAuthenticationProvider这个实现类下的authenticate()
之后会走到
user = retrieveUser(username,
(UsernamePasswordAuthenticationToken) authentication);
这个user是UserDetails 是最终返回的对象,对象里就包括了用户名,密码和所拥有的权限
UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
最终会调用我们自己去实现的接口UserDetails loadUserByUsername(String username)
比如我这个是一个装修公司端的登录,那么我这个类就继承了UserDetailsService的loadUserByUsername 然后在这里面进行账号密码的判断, 前端传过来的密码是明文的,在调用这个方法之前会使用内置的 PasswordEncoder
通常我们保存的密码都不会像之前介绍的那样,保存的明文,而是加密之后的结果。为此,我们的 AuthenticationProvider 在做认证时也需要将传递的明文密码使用对应的算法加密后再与保存好的密码做比较。Spring Security 对这方面也有支持。通过在 authentication-provider 下定义一个 password-encoder 我们可以定义当前 AuthenticationProvider 需要在进行认证时需要使用的 password-encoder。
从而进行验证。
6、然后我们返回到最开始的login的方法下
SecurityContextHolder.getContext().setAuthentication(authentication);
SecurityContextHolder:
作用:保留系统当前的安全上下文细节,其中就包括当前使用系统的用户信息。
每个用户都有自己的SecurityContext,存储在一个SecurityContextHolder中,整个应用就一个SecurityContextHolder。
/**
* 获取用户
**/
public static LoginUser getLoginUser()
{
try
{
return (LoginUser) getAuthentication().getPrincipal();
}
catch (Exception e)
{
throw new CustomException("获取用户信息异常", HttpStatus.UNAUTHORIZED);
}
}
/**
* 获取Authentication
*/
public static Authentication getAuthentication()
{
return SecurityContextHolder.getContext().getAuthentication();
}
这样就能获取到用户登录的信息。
6、
tokenService.setLoginUser(decorationUser);
// 生成token
return tokenService.createToken(decorationUser);
因为这个方法最终是返回一个token回去给前端,返回的信息有用户名和密码和权限
/**
* 创建令牌
*
* @param loginUser 用户信息
* @return 令牌
*/
public String createToken(LoginUser loginUser)
{
String token = IdUtils.fastUUID();
loginUser.setToken(token);
setUserAgent(loginUser); //设置用户代理信息
refreshToken(loginUser); //刷新令牌有效期
Map<String, Object> claims = new HashMap<>();
claims.put(Constants.LOGIN_USER_KEY, token);
return createToken(claims);
}
/**
* 从数据声明生成令牌
*
* @param claims 数据声明
* @return 令牌
*/
private String createToken(Map<String, Object> claims)
{
String token = Jwts.builder()
.setClaims(claims)
.signWith(SignatureAlgorithm.HS512, secret).compact();
return token;
}
最后:这是登录的大概,最终返回token给前端。解释不好的地方请各位看到这篇文章的大佬们指出。
补充信息:
Authenticatioin中包含了如下信息:
Authorities用户权限集合:可用于访问受保护资源时的权限验证
Credentials:证明委托人的凭据是正确的。这通常是一个密码,但可以是与Authentication Manager相关的任何内容。调用者被期望填充凭证。
Details细节:存储有关身份验证请求的其他详细信息。这些可能是ip地址、证书序列号等。
Pirncipal:被认证主体的身份。对于具有用户名和密码的authentication request,这就是用户名。调用者将填充身份验证请求的主体。Authentication Manager实现通常会返回一个包含丰富信息的身份验证,作为应用程序使用的主体。许多身份验证提供者将创建一个UserDetails对象作为主体。
isAuthenticated:是否已认证成功。
————————————————
版权声明:本文为CSDN博主「lifeifei2010」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/lifeifei2010/article/details/78787558