登陆业务(未加查缓存操作)
首先要讲一下面向切面编程也就是aop编程,我们在一个类前面加上@Aspect,就说明这是一个这个类中的方法是切面,@Aspect等于Advice与Joinpoint两部分组成。
@Aspect
@Component("needTokenAspect")
@Before("@annotation(com.akaedx.annotation.NeedToken)")//这是一个advice
在Spring AOP中支持4中类型的通知:
1:before advice 在方法执行前执行。
2:after returning advice 在方法执行后返回一个结果后执行。
3:after throwing advice 在方法执行过程中抛出异常的时候执行。
4:Around advice 在方法执行前后和抛出异常时执行,相当于综合了以上三种通知。
当一个方法执行的时候,Spring 能够拦截正在执行的方法,在方法执行的前或者后增加额外的功能和处理。
@annotation(com.akaedx.annotation.NeedToken)
@annotation就是表明了任何加@NeedToken注解的方法,这样系统就可以轻松找到需要token的方法,这个就是切点
我们找到切点后该怎么做呢,是不是要通过一定的方法去实现我们的业务啊
那么这个方法就是我们说的这个Joinpoint
public void NeedTokenDo(Joinpoint point){
Method method = ((MethodSignature) point.getSignature()).getMethod();
NeedToken needToken = method.getAnnotation(NeedToken.class);
try{
if(NeedToken==Null){
return;
}
if(needToken.checkLogin()){
checkLogin();
}
}catch(Throwable e){
throw new BussinessExecption(Code.DEFAULT_ERROR);
}
}
他其实就是一个切面,我们在由Spring注入一个point,然后每个前面标注@NeedToken的方法都会执行这样一个切面,去验证一下该方法是否需要Token,这就是切面编程的作用,把一些需要写在各个方法前的一些动作放在一个切面类里,在运行之前之后或者抛异常时执行,简化代码提高运行效率。
我们的业务为什么要用这样一个切面编程呢
比如说bilibili网页,有些业务比如收藏功能需要在登录之后去实现,这样我们在Controller这个包下可以写对应的方法,不过在这个方法前要加@NeedToken这个注解。
APP利用token机制进行身份认证
用户在登录APP时,APP端会发送加密的用户名和密码到服务器,服务器验证用户名和密码,如果验证成功,就会生成相应位数的字符产作为token存储到服务器中,并且将该token返回给APP端。
以后APP再次请求时,凡是需要验证的地方都要带上该token,然后服务器端验证token,成功返回所需要的结果,失败返回错误信息,让用户重新登录。其中,服务器上会给token设置一个有效期,每次APP请求的时候都验证token和有效期。
讲完了AOP编程,我们现在来讲具体登录流程
首先还是前端用户输入的信息变成JSON,经过反序列化后变成LoginRequestDTO(下面这个代码就是LoginRequestDTO这个类)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class LoginRequestDTO {
private String email;
private String password;
}
这个DTO就包含了,用户输入的账号密码,我们后端在收到这个DTO之后呢,要送到Controller层,先去验明账号密码是否跟数据库里的相同(本代码疑似没有写这个业务)
@PostMapping("/login")
public Response<SuccessLoginDTO> login(LoginRequestDTO requestDTO){
SuccessLoginDTO login=userService.login(requestDTO);
return Response.ok(login);
这个就是Controller层中关于登陆部分的方法
注解 @PostMapping("/login")
:这个注解表示这个方法会处理发送到 /login
路径的 HTTP POST 请求。就是如果这个业务是登录业务,那从前端发来的数据会首先发到Controller层的这个方法中。
SuccessLoginDTO login=userService.login(requestDTO);
如果没有问题的话就会给前端返回登陆成功这个信息
我们登录成功后是要给这个app页面一个token,用来验明下一次操作页面时是否登录
这句代码讲的是传进来的requestdto先被传到Controller层然后在Controller层调用userService这个接口内的方法去比较数据库里的数据
public interface UserService {
void regisiter(RegisterRequestDTO requestDTO);
SuccessLoginDTO login(LoginRequestDTO loginRequestDTO);
}
注:UserService接口里的SuccessLoginDTO与userController里的SuccessLoginDTO不一样,前者是以下的方法Login的返回类型,后者是对象
@Override
public SuccessLoginDTO login(LoginRequestDTO loginRequestDTO) {
QueryWrapper<UserInfo> queryWrapper=new QueryWrapper<>();
queryWrapper
.lambda()
.eq(UserInfo::getEmail,loginRequestDTO.getEmail())
.eq(UserInfo::getPassword,StringTools.encodeMd5((loginRequestDTO.getPassword())));
UserInfo userInfo=userInfoMapper.selectOne(queryWrapper);
if(userInfo ==null){
throw new BusinessException(CodeEnum.LOGIN_FAILED_01);
}
SuccessLoginDTO successLoginDTO=new SuccessLoginDTO();
successLoginDTO.setLoginUser(SuccessLoginDTO.LoginUser.PO2LOGIN_UESER(userInfo));
String token = UUID.randomUUID().toString();
successLoginDTO.setToken(token);
redisComment.token2UserId(token, successLoginDTO.getLoginUser().getId());
redisComment.userId2SuccessLoginDTO(successLoginDTO.getLoginUser().getId(), successLoginDTO);
return successLoginDTO;
}
这个Login方法最终返回类型是SuccessLoginDTO,在登录信息从前端传到Controller层之后,Controller层调用Service层的接口也就是login方法去比较,如果错误就抛异常,没问题的话会创建一个SuccessLoginDTO引用,把没问题的UserInfo相关信息赋给SuccessLoginDTO,因为SuccessLogin这个类含loginUser与token,所以我们在这个方法中用UUID生成了一串字符来作为token一并赋给successLoginDTO,然后返回successLoginDTO
return Response.ok(login);
最后回到cotroller层把SuccessLoginDTO类型的login与返回类的codeEnum一并返回到前端(如下图所示)