All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.lx.boot.web.DefaultSecurityInterceptor Maven / Gradle / Ivy

Go to download

使用文档: https://a7fi97h1rc.feishu.cn/docx/X3LRdtLhkoXQ8hxgXDQc2CLOnEg?from=from_copylink

There is a newer version: 1.1
Show newest version
package com.lx.boot.web;


import com.lx.annotation.Note;
import com.lx.boot.OS;
import com.lx.boot.web.annotation.Auth;
import com.lx.constant.DefaultResult;
import com.lx.entity.ExpireCache;
import com.lx.entity.Result;
import com.lx.entity.UserInfo;
import com.lx.util.LX;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import java.util.Optional;

import static com.lx.constant.DefaultBaseConstant.*;


/**
 * @author ylx
 * @description 权限拦截器
 * @date 2017/11/17
 * @since 1.0
 */
@Slf4j
public class DefaultSecurityInterceptor implements HandlerInterceptor {

    @Autowired
    private AuthUtil authUtil;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        String url = request.getServletPath();
        //根据token查询用户信息
        UserInfo userInfo = OS.getRealUserInfo();
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Auth auth = Optional.ofNullable(handlerMethod.getMethod().getAnnotation(Auth.class)).orElse(handlerMethod.getMethod().getDeclaringClass().getAnnotation(Auth.class));
        if (auth != null && !checkAuth(auth, userInfo, url,request,response)){
            return false;
        }
        // 用户不存在
        if (LX.isEmpty(userInfo)){
            //开发环境
            if (isDev(request)){
                return true;
            }
            //获取方法上或者类上的注解
            if (auth != null && !auth.value()){
                return true;
            }
            //无需登录的接口
            if (isNotAuthUrl(url)){
                return true;
            }
            // 判断是不是系统接口
            if (isSysMethod(request, auth)){
                OS.allowAccessKeyClear(url);
                return true;
            }
            log.warn("请重新登录! 请检查是否使用 AuthUtil.createToken/createJwtToken 创建用户登录信息!");
            setReturnInfo(response, DefaultResult.TOKERPAST.getResult());
            return false;
        }
        return true;
    }

    /** 说明: 无需登录的接口
     * @author ylx 2023/3/10 14:02 */
    public static boolean isNotAuthUrl(String url) {
        //无需登录接口
        String notAuthPattern = OS.getProperty(SERVER_NOTAUTH);
        if ((LX.isNotEmpty(notAuthPattern) && url.matches(notAuthPattern))){
            return true;
        }
        //无需登录加密接口 或 第三方接口
        return SecurityFilter.isNotAuthAndEncryptionUrl(url) || SecurityFilter.isApiUrl(url);
    }

    @Note("缓存系统接口的nonce, 分布式部署需要使用redis")
    private static ExpireCache nonceExpireCache = new ExpireCache(5000,600_000);
    //说明: 内置方法进行认证 通过header sysAuthorization 传 des(密码+时间戳) 进行对比
    /** @author ylx 2022/12/2 16:30 */
    private boolean isSysMethod(HttpServletRequest request, Auth auth) {
        if (auth != null && auth.isSysMethod()){
            try {
                if(!OS.allowAccessTo(request.getServletPath(),"5","300")){
                    //300秒内错误5次就锁定不能继续调用
                    return false;
                }
                String key = request.getHeader(SYS_AUTHORIZATION);
                String timestamp = request.getHeader(SYS_AUTHORIZATION_TIMESTAMP);
                String nonce = request.getHeader(SYS_AUTHORIZATION_NONCE);
                if (LX.isEmpty(key) || LX.isEmpty(nonce) || LX.isEmpty(timestamp)){
                    return false;
                }
                if (nonceExpireCache.get(nonce) != null){
                    return false;
                }
                nonceExpireCache.put(nonce,"1");
                Long t = Long.parseLong(timestamp);
                boolean forMoreThan = (System.currentTimeMillis() - t)/(1000*60) > 15;
                LX.exMsg(forMoreThan,"时间大于15分钟,不能调用接口");
                String dKey = LX.desEncrypt(OS.getProperty(SERVER_USER_KEY, DEFAULT_SERVER_USER_KEY) + timestamp+"-"+nonce, "yzy@123@");
                return dKey.equals(key);
            }catch (Exception e){
                log.error("判断系统接口登录异常:",e);
            }
        }
        return false;
    }

    //说明: 对@Auth特殊判断
    /** @author ylx 2022/8/26 10:40 */
    private boolean checkAuth(Auth auth, UserInfo userInfo, String url, HttpServletRequest request, HttpServletResponse response) throws InstantiationException, IllegalAccessException {
        // 判断IP
        if (LX.isNotEmpty(auth.ipWhiteListRegexApplicationKey())){
            String ip = OS.getIp(request);
            String ipWhiteList = OS.getProperty(auth.ipWhiteListRegexApplicationKey());
            if (LX.isEmpty(ip) || LX.isEmpty(ipWhiteList) || !ip.matches(ipWhiteList)){
                log.info("ip:{}, 接口IP注解配置:{}, 实际配置:{}",ip, auth.ipWhiteListRegexApplicationKey(),ipWhiteList);
                setReturnInfo(response, DefaultResult.IP_LIMIT.setMessage("调用IP异常:"+ip));
                return false;
            }
        }
        // 根据IP限制方法调用次数
        if (LX.isNotEmpty(auth.callNumberByIp())){
            String ip = OS.getIp(request);
            if (!OS.allowAccessByCallFrequency(ip+":"+url, auth.callNumberByIp())){
                setReturnInfo(response, DefaultResult.CALL_LIMIT.setMessage("调用次数超过限制:"+auth.callNumberByIp()));
                return false;
            }
        }
        if (LX.isNotEmpty(userInfo)){
            // 判断是否有权限
            if (LX.isNotEmpty(auth.allowUserTypeRegex())){
                if (LX.isEmpty(userInfo.getUserType()) || !userInfo.getUserType().matches(auth.allowUserTypeRegex())){
                    log.info("用户信息:{}, 接口注解配置:{}",userInfo, auth.allowUserTypeRegex());
                    setReturnInfo(response, DefaultResult.ACCESSERROR.getResult());
                    return false;
                }
            }
            // 根据用户ID限制方法调用次数
            if (LX.isNotEmpty(auth.callNumberByUser())){
                if (!OS.allowAccessByCallFrequency(userInfo.getUserId()+":"+url, auth.callNumberByUser())){
                    setReturnInfo(response, DefaultResult.CALL_LIMIT.setMessage("调用次数超过限制:"+auth.callNumberByUser()));
                    return false;
                }
            }
        }
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
//        log.info("结束调用微服务渲染视图");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    }

    //说明:开发调试环境
    /**{ ylx } 2021/6/15 16:26 */
    private static boolean isDev(HttpServletRequest request){
        return "dev".equals(OS.getEnv())  && "0".equals(request.getHeader(AUTHORIZATION));
    }


    private void setReturnInfo(HttpServletResponse response, Result info){
        try {
            //返回响应
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write(LX.toJSONString(info));
        } catch (Exception e) {
            log.error("过滤器拦截失败!",e);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy