spring AOP+注解方式实现登录验证(session+cookies)

学习总是在不断继续的,这次我将实现一个基于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+注解,所以在很多逻辑上的东西还没有进行完善(完善可能要等以后的文章,但是遥遥无期......),如果各位觉得我写的有问题,请大方指正,不胜感激!

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
首先,我们需要定义一个自定义注解 `@RequiresPermissions`,用于标识需要授权访问的方法,例如: ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface RequiresPermissions { String[] value(); // 权限值 } ``` 然后,我们需要实现一个切面,用于拦截被 `@RequiresPermissions` 标识的方法,并进行权限校验,例如: ```java @Component @Aspect public class PermissionCheckAspect { @Autowired private AuthService authService; @Around("@annotation(requiresPermissions)") public Object checkPermission(ProceedingJoinPoint joinPoint, RequiresPermissions requiresPermissions) throws Throwable { // 获取当前用户 User user = authService.getCurrentUser(); if (user == null) { throw new UnauthorizedException("用户未登录"); } // 获取当前用户的权限列表 List<String> permissions = authService.getUserPermissions(user); // 校验权限 for (String permission : requiresPermissions.value()) { if (!permissions.contains(permission)) { throw new ForbiddenException("没有访问权限:" + permission); } } // 执行目标方法 return joinPoint.proceed(); } } ``` 在切面中,我们首先通过 `AuthService` 获取当前用户及其权限列表,然后校验当前用户是否拥有被 `@RequiresPermissions` 标识的方法所需的所有权限,如果没有则抛出 `ForbiddenException` 异常,如果有则继续执行目标方法。 最后,我们需要在 Spring 配置文件中启用 AOP 自动代理,并扫描切面所在的包,例如: ```xml <aop:aspectj-autoproxy /> <context:component-scan base-package="com.example.aspect" /> ``` 这样,我们就通过 Spring AOP 和自定义注解模拟实现了类似 Shiro 权限校验的功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

matthew_leung

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值