直捣黄龙,我直接攻破公司的认证框架调用系统的接口

本文介绍如何在使用Shiro安全架构时,通过创建自定义AuthFilter实现免登录功能,仅凭账号访问特定接口,同时利用JWT为请求添加权限验证。作者详细讲解了Filter的工作原理和关键代码实现。

公司使用的安全架构是Shiro,所以每访问一个请求都需要带一个令牌去访问这个接口,否则就是被拦截,其实我们可以在配置类中把要访问的接口给放开,允许通行:

public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
    
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();


        // Shiro的核心安全接口,这个属性是必须的
        shiroFilterFactoryBean.setSecurityManager(securityManager);
     
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
   
        filterChainDefinitionMap.put("/user_curriculum_vitae/pull", "anon");


        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return shiroFilterFactoryBean;
    }

类似上面这样我们就把user_curriculum_vitae这个接口给放开了。
但是!!这就意味着他获取不到令牌了,没有令牌只有也意味着获取不到用户的信息,这样系统在进行用户信息的时候就会报错,这样我们有什么更好的方法呢?类似想实现一个免登录的功能?只需要账号即可跳过验证,拿到用户的信息。
这里我们就要请出今天的主角:Filter
这里类是在javax.servlet包下,也就是说是java提供的最原始的过滤器接口,所以我们可以实现这个接口:


@WebFilter
@Component
public class AuthFilter implements Filter {

    @Autowired
    private CmcServerFeigin cmcServerFeigin;

    /**
     * 要拦截的请求
     *
     * @Param:
     * @return:
     * @Author: MaSiyi
     * @Date: 2022/2/11
     */
    private final String[] filter = {
            "/ddqr/autoCreatedFlow",
            "/autoCreateFlow/handleCreateFlow",
            "/autoCreateFlow/normalCreateFlow"
    };

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        String requestURI = request.getRequestURI();

        List<String> list = Arrays.asList(filter);

        if (list.contains(requestURI)) {
            filterChain.doFilter(setKeyId(request), response);
        } else {
            filterChain.doFilter(request, response);
        }
    }

    /**
     * 往请求头添加 keyId
     *
     * @param request
     * @return
     */
    public RHttpServletRequest setKeyId(HttpServletRequest request) {
        RHttpServletRequest rHttpServletRequest = new RHttpServletRequest(request);
        //获取
        JSONObject obj = new JSONObject();
        ResultUtils res;
        String psnCode = rHttpServletRequest.getHeader("psnCode");
        if (!StringUtils.isEmpty(psnCode)) {
            obj.put("signature", psnCode);
        }
        res = cmcServerFeigin.getToken(obj);
        String keyId = "";
        if (CodeEnums.SUCCESS_CODE.getCode().equals(res.getCode())) {
            JSONObject jsonObject = JsonUtils.toJsonObject(res.getData());
            String token = jsonObject.getString("token");
            keyId = token == null ? jsonObject.getString("keyId") : token;
        } else {
            System.out.println("获取keyId失败:" + JsonUtils.toJsonObject(res));
        }
        rHttpServletRequest.addHeader("keyId", keyId);

        return rHttpServletRequest;
    }

    @Override
    public void destroy() {

    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

}

这里我们一个要实现三个抽象方法:

doFilter //过滤器 destroy //销毁的时候调用 init // 初始化的时候调用

在doFilter方法中我们,可以对请求头和返回体做响应,其实可以理解为一个拦截器
而在

    private final String[] filter = {
            "/autoCreateFlow/handleCreateFlow",
            "/autoCreateFlow/normalCreateFlow"
    };

这个数组中,我们可以理解他为过滤器,这里有一个面试官常常问的问题:

你了解过拦截器和过滤器吗?

其实按照我的理解就是拦截器就是对请求在进入方法之前我们可以对他们进行一些特殊的操作,例如修改请求体等等,而过滤器呢就是可以过滤掉一些请求去访问我们的方法,例如过滤掉某些不需要认证的接口,如springsecurity中放行的接口。

所以上面这个类的作用就是,凡是在我filter数组中的请求我们就可以对他们进行一些操作,这里的操作就是在他们的request中添加对应的授权信息,这样就使得别人调用接口的时候就可以实现权限的认证了。

但是!在上面的HttpServletRequest类中他是没有添加请求头的方法的,所以这里我们要重写一个类用来给请求头添加信息:


public class RHttpServletRequest extends HttpServletRequestWrapper {

    private Map<String, String> headerMap = new HashMap<>();

    public RHttpServletRequest(HttpServletRequest request) {
        super(request);
    }


    @Override
    public String getParameter(String name) {
        return super.getParameter(name);
    }

    /**
     *
     * 添加具有给定名称和值
     * @param name
     * @param value
     */
    public void addHeader(String name, String value) {
        headerMap.put(name, value);
    }

    @Override
    public String getHeader(String name) {
        String headerValue = super.getHeader(name);
        if (headerMap.containsKey(name)) {
            headerValue = headerMap.get(name);
        }
        return headerValue;
    }

    @Override
    public Enumeration<String> getHeaderNames() {
        List<String> names = Collections.list(super.getHeaderNames());
        for (String name : headerMap.keySet()) {
            names.add(name);
        }
        return Collections.enumeration(names);
    }

    @Override
    public Enumeration<String> getHeaders(String name) {
        List<String> values = Collections.list(super.getHeaders(name));
        if (headerMap.containsKey(name)) {
            values = Arrays.asList(headerMap.get(name));
        }
        return Collections.enumeration(values);
    }

}

这样就能够实现对于请求头的相关操作啦!
如果你看到这里,希望给博主一个点赞收藏加关注哦!!

直捣黄龙”是PTA(Programming Teaching Assistant,程序设计类课程辅助教学平台)上的一道题目,以下是对该题目的相关分析: ### 题目描述 本题是关于图的路径搜索问题,背景设定为岳飞带领岳家军直捣黄龙府。给定一张地图,地图上有多个城市,城市之间有道路相连且每条道路有一定的距离。需要从起点“临安”出发,找到一条满足特定条件的路径到达“黄龙”。 ### 条件要求 1. **最短路径**:在所有从起点到终点的路径中,优先选择总距离最短的路径。 2. **最多杀敌数**:如果存在多条最短路径,选择路径上经过的城市中杀敌总数最多的路径。 3. **平均杀敌数最多**:若还有多条路径满足上述条件,选择路径上平均每个城市杀敌数最多的路径(平均杀敌数 = 总杀敌数 / (路径上城市数 - 1))。 ### 解题思路 1. 构建图:使用邻接表或邻接矩阵来表示城市之间的连接关系和距离。 2. 深度优先搜索(DFS)或迪杰斯特拉(Dijkstra)算法:用于寻找最短路径。 3. 记录路径信息:在搜索过程中,记录每条路径的总距离、总杀敌数和经过的城市。 4. 筛选最优路径:根据题目要求的条件,筛选出最优路径。 ### 代码示例(Python) ```python import heapq from collections import defaultdict # 读取输入 n, k = map(int, input().split()) cities = input().split() city_index = {city: i for i, city in enumerate(cities)} enemies = list(map(int, input().split())) # 构建图 graph = defaultdict(list) for _ in range(k): u, v, d = input().split() u_index = city_index[u] v_index = city_index[v] d = int(d) graph[u_index].append((v_index, d)) graph[v_index].append((u_index, d)) start = city_index["临安"] end = city_index["黄龙"] # 迪杰斯特拉算法 dist = [float('inf')] * n dist[start] = 0 ways = [0] * n ways[start] = 1 kill = [0] * n path_count = [0] * n pre = [-1] * n pq = [(0, start)] while pq: cur_dist, cur = heapq.heappop(pq) if cur_dist > dist[cur]: continue for neighbor, d in graph[cur]: new_dist = cur_dist + d if new_dist < dist[neighbor]: dist[neighbor] = new_dist ways[neighbor] = ways[cur] kill[neighbor] = kill[cur] + enemies[neighbor] path_count[neighbor] = path_count[cur] + 1 pre[neighbor] = cur heapq.heappush(pq, (new_dist, neighbor)) elif new_dist == dist[neighbor]: ways[neighbor] += ways[cur] if kill[cur] + enemies[neighbor] > kill[neighbor]: kill[neighbor] = kill[cur] + enemies[neighbor] path_count[neighbor] = path_count[cur] + 1 pre[neighbor] = cur elif kill[cur] + enemies[neighbor] == kill[neighbor]: if (kill[cur] + enemies[neighbor]) / (path_count[cur] + 1) > kill[neighbor] / path_count[neighbor]: path_count[neighbor] = path_count[cur] + 1 pre[neighbor] = cur # 输出结果 path = [] cur = end while cur != -1: path.append(cur) cur = pre[cur] path.reverse() print(ways[end], dist[end], kill[end], kill[end] // (len(path) - 1)) print(" -> ".join(cities[i] for i in path)) ``` ### 复杂度分析 - **时间复杂度**:$O(V^2)$ 或 $O((V + E) \log V)$,取决于使用的算法和数据结构。 - **空间复杂度**:$O(V + E)$,主要用于存储图和路径信息。 ### 相关问题
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

掉头发的王富贵

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

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

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

打赏作者

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

抵扣说明:

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

余额充值