zl程序教程

您现在的位置是:首页 >  其他

当前栏目

ruoyi-vue版本(四)@PreAuthorize 注解在若依里面的作用,springsecurity 框架相关的配置

2023-02-26 09:50:16 时间

目录

1 需求

我们打开若依项目,发现一些接口上面是有@PreAuthorize 注解,那么这个注解的作用是什么?

2 实现

2.1 背景

我们点进去@PreAuthorize 注解 里面的源码,发现他是第三方的jar包里面的,

我们都知道,若依项目是集成了 springsecurity 框架,所以这个注解直接使用的是这个 springsecurity 框架里面的,我们拿上这个注解使用就可以,那么他咋使用,底层逻辑是什么?

2.2 实现

既然若依项目是集成了 springsecurity 框架,那么肯定需要写springsecurity 的配置类,我们找一下

若依项目集成了很多的第三方的jar包,所以配置类是比较多,在这个ruoyi-framework 模块下,是有一个config文件夹,下面就是放的各种各样jar包的配置文件,我们找到和 springsecurity 框架相关的配置文件。

2.3 springsecurity 框架相关的配置

2.3.1 @EnableGlobalMethodSecurity详解

当我们想要开启spring方法级安全时,只需要在任何 @Configuration实例上使用 @EnableGlobalMethodSecurity 注解就能达到此目的。同时这个注解为我们提供了prePostEnabled 、securedEnabled 和 jsr250Enabled 三种不同的机制来实现同一种功能。

A. prePostEnabled = true:
会开启 @PreAuthorize 和 @PostAuthorize 两个注解。

@PreAuthorize注解会在方法执行前进行验证,
支持Spring EL表达式;
@PostAuthorize 注解会在方法执行后进行验证,
不经常使用, 适用于验证带有返回值的权限。

Spring EL提供了returnObject,
用于能够在表达式语言中获取返回的对象信息;
B. securedEnabled = true:
会开启@Secured 注解,用来定义业务方法的安全配置,
在调用的接口或方法上使用该注解。

在需要安全控制(一般使用角色或者权限进行控制)的方法上指定
@Secured,达到只有具备那些角色/权限的用户才可以访问该方法。

指定角色时必须以ROLE_开头,不可省略;不支持Spring EL表达式;
如果想要使用@Secured注解指定"AND"条件,
即调用deleteAll方法需同时拥有ADMIN和DBA角色的用户时,
@Secured便不能实现,

只能使用@PreAuthorize/@PostAuthorize注解。

2.3.2 认证失败处理类AuthenticationEntryPointImpl

这个类是在spring security 配置类里面进行引用的,所以我们先看这个类里面干了什么事情

认证失败处理类AuthenticationEntryPointImpl 实现了AuthenticationEntryPoint接口,重写了这个接口的方法

它在用户请求处理过程中遇到认证异常时,被ExceptionTranslationFilter用于开启特定认证方案(authentication schema)的认证流程。

这里参数request是遇到了认证异常authException用户请求,response是将要返回给客户的相应,方法commence实现,也就是相应的认证方案逻辑会修改response并返回给用户引导用户进入认证流程。

也就是认证失败,就会自动的进方法commence方法里面,走里面的
逻辑;

修改response并返回给用户引导用户进入认证流程。这个是主要的
目的

2.3.3 自定义退出处理类LogoutSuccessHandlerImpl

这个类实现了LogoutSuccessHandler接口,重写了里面的onLogoutSuccess方法,并且这个类上面是@Configuration; 说明当前类是全局配置类;

LogoutSuccessHandler接口是Spring Security框架里面的 ;

为什么要实现这个接口?

大多数业务场景下,自定义登出成功页面也满足不了一些要求,
更别提默认的登出成功页面。这时候,就需要别的方案支持
,幸运的是,Spring Security 框架还真就非常贴心的提供了
这样一个接口 LogoutSuccessHandler, 专门用于处理用户
登出成功请求。当然了,对于 LogoutSuccessHandler 接口,
Spring Security 框架有一些默认的实现,也可以自行扩展。


同用户登录成功的业务场景,在用户登出成功后,我们也要通过邮件、
短信、微信,来通知用户,在什么时间,什么地点,退出了系统。
更甚至,可以通知用户本次登录都操作了那些功能,做了哪些操作
等等。同时,也要把这些信息记录在日志文件中。而这所有的需求,
都可以通过实现 LogoutSuccessHandler 接口来实现。


扩展 LogoutSuccessHandler 接口实现发送信息、日志记录。
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
    super.onLogoutSuccess(request, response, authentication);
​
    this.logger.info(String.format("IP %s,用户 %s, 于 %s 退出系统。", request.getRemoteHost(), authentication.getName(), LocalDateTime.now()));
​
    try {
        // 发邮件
        this.emailService.send();
​
        // 发短信
        this.smsService.send();
​
        // 发微信
        this.weChatService.send();
    } catch (Exception ex) {
        this.logger.error(ex.getMessage(), ex);
    }
}

目前若依项目,当退出的时候,所做的事情是在redis里面清空token,之后异步记录一下日志;

package com.ruoyi.framework.security.handle;

import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import com.alibaba.fastjson2.JSON;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.HttpStatus;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.framework.manager.factory.AsyncFactory;
import com.ruoyi.framework.web.service.TokenService;

/**
 * 自定义退出处理类 返回成功
 * 也就是用户退出系统,在退出之前会默认做的事情
 * @author jing
 */
@Configuration
public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler
{
//    将token的工具类 引入
    @Autowired
    private TokenService tokenService;

    /**
     * 退出处理
     * 
     * @return
     */
    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
            throws IOException, ServletException
    {
//        根据request  获取到用户的信息
        LoginUser loginUser = tokenService.getLoginUser(request);
        if (StringUtils.isNotNull(loginUser))
        {
            String userName = loginUser.getUsername();
            // 删除用户缓存记录    redis  里面进行删除
            tokenService.delLoginUser(loginUser.getToken());
            // 记录用户退出日志
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(userName, Constants.LOGOUT, "退出成功"));
        }

//        直接渲染信息到客户端
        ServletUtils.renderString(response, JSON.toJSONString(AjaxResult.error(HttpStatus.SUCCESS, "退出成功")));
    }
}

2.3.4 filter 为啥会多次执行

在servlet2.3中,Filter会经过一切请求,包括服务器内部使用的forward转发请求和<%@ include file=”/login.jsp”%>的情况

servlet2.4中的Filter默认情况下只过滤外部提交的请求,forward和include这些内部转发都不会被过滤

 就是有可能一个接口里面,使用了forward和include这些内部转发;
 然后继续  执行过滤器

3 @PreAuthorize 注解

通过在PreAuthorize表达式的Bean名称开头添加@来引用注册为组件的Bean。