学习总是在不断继续的,这次我将实现一个基于aop的的一个注解操作。
首先加依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
1.我们第一步就是要定义一个注解:
package com.shengxi.compent.aop.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 登录状态验证
*
* @author matthew
* @version 1.0.0
* @date 2019-11-26 12:29:20
* @see java.lang.annotation.Annotation
*/
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginStatus {
boolean value() default false;
}
这里使用一个value参数,主要也是应该自己想更多的记录关于注解和aop联合使用的知识点。事实上可以直接使用一个注解就好了,因为再解析一个参数,而不做任何有意义的操作。就是在浪费资源。
2、我们一定要理解一个问题,就是我们想要实现利用aop验证是否登录,就必须要进行登录状态的保存和更新。所以我进行了一个垃圾的登录状态保存和更新。
/**
* 将用户名保存在session中
*
* @param request 请求
* @param loginUser 登录用户
*/
private Cookie saveToSessionAndCookies(HttpServletRequest request, User loginUser) {
/* 将信息保存在session中,避免登录有效被篡改和私密信息被截取。 */
String sha1 = SecureUtil.sha1(loginUser.getName());
HttpSession session = request.getSession();
session.setAttribute(sha1, new SessionMsgPojo(LocalDateTime.now(), loginUser.getName()));
/*cookies只保留用户名的sha1值*/
return new Cookie("user", sha1);
}
这是在登录的时候将登陆状态信息保存在session,然后利用sha1哈希散列进行摘要生成。这样cookies保存的数据就不用担心被篡改了。然后sha1的加密强度不是很大,但是我们很多时候做出来的项目但是小型项目,不用担心会被资金充足的机构进行攻击。所以加密方案可以自行选择,这里只是一种作者在学习到这个时间点时的一个安全意识体现,而没有强制要求。
3、我们会对登录状态进行一个可持续的更新:
package com.shengxi.compent.utils.filter;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.shengxi.carblog.pojo.weak.SessionMsgPojo;
import java.io.IOException;
import java.time.LocalDateTime;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
/**
* 自动更新登录有效期
* @author matthew
* @version 1.0.0
* @date 2019-11-27 23:47:09
* @see javax.servlet.Filter
*/
@WebFilter(filterName = "LoginTimeFilter")
public class LoginTimeFilter implements Filter {
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
/*获取cookies*/
Cookie[] cookies = ((HttpServletRequest) req).getCookies();
HttpSession session = ((HttpServletRequest) req).getSession();
for (Cookie cookie : cookies) {
String value = cookie.getValue();
SessionMsgPojo attribute = (SessionMsgPojo) session.getAttribute(value);
if (ObjectUtil.isNotNull(attribute)) {
attribute.setLocalDateTime(LocalDateTime.now());
}
}
chain.doFilter(req, resp);
}
}
这里会对已经登录的会话进行一个最后一次会话时间进行记录使其两个小时内不需要进行多次登录。选择使用过滤器进行登录的状态维持,是因为本着先实现,再优化的思想进行写bug。所以我也没有考虑那么多性能问题,而是先用一个简单的过滤器去实现这个登录时间的维持。
下面是aop源码:
package com.shengxi.compent.aop;
import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.shengxi.compent.aop.annotation.LoginStatus;
import javax.servlet.ServletContext;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.servlet.ModelAndView;
/**
* @author yan
* @version 1.0.0
* @date 2019-11-26 17:00:23
*/
@Aspect
@Component
public class LoginStatusAspect {
private final Logger logger = LoggerFactory.getLogger(LoginStatusAspect.class);
@Pointcut()
public void login() {
}
/**
* 围绕注解进行切面编程
*
* @param joinPoint join point
* @return
* @throws Exception ex
*/
@Around("@annotation(com.shengxi.compent.aop.annotation.LoginStatus)")
public Object aroundManager(ProceedingJoinPoint joinPoint) throws Throwable {
LoginStatus annotation = ((MethodSignature) joinPoint.getSignature()).getMethod().getAnnotation(LoginStatus.class);
if (annotation.value()){
boolean flag =false;
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
Cookie[] cookies = request.getCookies();
HttpSession session = request.getSession();
if (ObjectUtil.isNotEmpty(cookies) && ObjectUtil.isNotEmpty(session)) {
for (Cookie cookie : cookies) {
if (ObjectUtil.isNotEmpty(session.getAttribute(cookie.getValue()))) {
flag = !flag;
}
}
}
if (BooleanUtil.isFalse(flag)){
return new ModelAndView("/login");
}
}
return joinPoint.proceed();
}
}
这里aop是环绕注解进行解析,然后获取注解中的参数(value),然后获取请求中的session和cookies。我还知道一种是利用ThreadLocal实现拦截器获取对应的ServletRequest和ServletResponse的方案,但是我觉得能直接使用接口就不要这么勤快的自己再写一个实体类和拦截器去实现了。毕竟这次学习的重点不是那个point。最后在session中没有对应数据的就返回到登录页面。
总结:
这次写的主要是为了aop+注解,所以在很多逻辑上的东西还没有进行完善(完善可能要等以后的文章,但是遥遥无期......),如果各位觉得我写的有问题,请大方指正,不胜感激!