【spring oauth2】spring oauth2添加自定义拦截验证

前言

在看其他文章,实现了sso之后,发现通过security配置的拦截失效了,没有起作用。
接着在查找oauth2拦截配置的过程中,有人说到AccessDecisionManager。通过它来配置拦截。
下面开始实现。

查找资料

搜索关键字:AccessDecisionManager配置
参考文档:

搜到了很多,有的可以,有的不行。影响不大的稍微改下就行了。
最后,发现这篇合适http://www.mamicode.com/info-detail-2578727.html
已经贴到项目中,下面开始实现。

开始实现

首先,参考文档,略做修改,实现了基本的拦截
文档:http://www.mamicode.com/info-detail-2578727.html
原文:https://www.cnblogs.com/Mr-XiaoLiu/p/10231542.html
摘自原文:
在Spring Security中实现通过数据库动态配置url资源权限,需要通过配置验证过滤器来实现资源权限的加载、验证。系统启动时,到数据库加载系统资源权限列表,当有请求访问时,通过对比系统资源权限列表和用户资源权限列表(在用户登录时添加到用户信息中)来判断用户是否有该url的访问权限。

在配置验证过滤器时需要的配置项有如下几个:
filterSecurityInterceptor:通过继承AbstractSecurityInterceptor并实现Filter接口自定义一个验证过滤器,替换默认验证过滤器。
accessDecisionManager:通过实现AccessDecisionManager接口自定义一个决策管理器,判断是否有访问权限。判断逻辑可以写在决策管理器的决策方法中,也可以通过投票器实现,除了框架提供的三种投票器还可以添加自定义投票器。自定义投票器通过实现AccessDecisionVoter接口来实现。
securityMetadataSource:实现FilterInvocationSecurityMetadataSource接口,在实现类中加载资源权限,并在filterSecurityInterceptor中注入该实现类。
WebSecurityConfig:系统配置类,需要在配置类中配置启用filterSecurityInterceptor。


下面是代码
MyAccessDecisionManager

import org.apache.commons.collections.CollectionUtils;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Service;

import java.util.Collection;

@Service
public class MyAccessDecisionManager implements AccessDecisionManager {
    /**
     * 决策方法:权限判断
     *
     * @param authentication   用户的身份信息;
     * @param object           包含客户端发起的请求的request信息,可转换为 HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
     * @param configAttributes 是MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法返回的结果,
     *                         此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限;如果不在权限表中则放行。
     * @throws AccessDeniedException
     * @throws InsufficientAuthenticationException
     */
    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {

        if (CollectionUtils.isEmpty(configAttributes)) {
            throw new AccessDeniedException("没有权限");
        }
        for (GrantedAuthority ga : authentication.getAuthorities()) {
            String authority = ga.getAuthority();
            if (configAttributes.contains(new SecurityConfig(ga.getAuthority()))) {
                return;
            }
        }
        throw new AccessDeniedException("没有权限");
    }

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return true;
    }

}

MyFilterInvocationSecurityMetadataSource

import com.core.server.entity.BasePrivilege;
import com.oauth2.server.system.init.feign.AllFeignServiceApi;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;

@Service
public class MyFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
    @Autowired
    AllFeignServiceApi allFeignServiceApi;
    /**
     * 资源权限
     */
    private volatile HashMap<String, Collection<ConfigAttribute>> urlPermMap = null;

    /**
     * 加载资源,初始化资源变量
     */
    public void loadResourceDefine() {
        List<BasePrivilege> basePrivileges = allFeignServiceApi.baseUserFeignServiceApi.queryAllBasePrivilege();
        if(CollectionUtils.isNotEmpty(basePrivileges)){
            urlPermMap = new HashMap<>();
            basePrivileges.forEach(item->{
                if(StringUtils.isNotBlank(item.getPath())){
                    List<ConfigAttribute> authorityList = new ArrayList<>();
                    ConfigAttribute auth = new SecurityConfig(String.format("%s-%s", item.getResourceCode(), item.getCode()));
                    authorityList.add(auth);
                    urlPermMap.put(item.getPath(), authorityList);
                }
            });
        }
    }

    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        loadResourceDefine();
        FilterInvocation fi = (FilterInvocation) object;
        String url = fi.getRequestUrl();

        // 资源权限为空,初始化资源
        if (null == urlPermMap) {
            synchronized (MyFilterInvocationSecurityMetadataSource.class) {
                if (null == urlPermMap) {
                    loadResourceDefine();
                }
            }
        }

        return urlPermMap.get(url);
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> clazz) {
        return FilterInvocation.class.isAssignableFrom(clazz);
    }
}

MyFilterSecurityInterceptor

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityMetadataSource;
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.FilterInvocation;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Collection;

public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
    @Autowired
    private MyFilterInvocationSecurityMetadataSource myFilterInvocationSecurityMetadataSource;
    @Autowired
    MyAccessDecisionManager myAccessDecisionManager;

    @Autowired
    public void setMyAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) {
        super.setAccessDecisionManager(myAccessDecisionManager);
    }

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

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        FilterInvocation fi = new FilterInvocation(request, response, chain);
        invoke(fi);
    }

    /**
     * @param fi 里面有一个被拦截的url,调用MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取fi对应的所有权限,
     *           再调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够
     * @throws IOException
     * @throws ServletException
     */
    public void invoke(FilterInvocation fi) throws IOException, ServletException {
        InterceptorStatusToken token = super.beforeInvocation(fi);
        try {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            Collection<ConfigAttribute> attributes = myFilterInvocationSecurityMetadataSource.getAttributes(fi);
            HttpServletRequest request = fi.getHttpRequest();
            myAccessDecisionManager.decide(authentication, request, attributes);
            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
        } finally {
            super.afterInvocation(token, null);
        }
    }

    @Override
    public void destroy() {

    }

    @Override
    public Class<?> getSecureObjectClass() {
        return FilterInvocation.class;
    }

    @Override
    public SecurityMetadataSource obtainSecurityMetadataSource() {
        return this.myFilterInvocationSecurityMetadataSource;
    }
}

代码添加完毕,可以给过滤器添加断点,会发现请求可以会拦截到。但是想要正式使用,还要加一些逻辑判断。

上面这些代码中,主要有这以下几点,是我这边自己补充的:

  • 第一点,urlPermMap。这里是参考这篇文章:https://www.cnblogs.com/weilu2/p/springsecurity_custom_decision_metadata.html
    其次,MyFilterSecurityInterceptor.invoke方法做了实现。根据方法上面的注释,添加了代码。

    /**
         * @param fi 里面有一个被拦截的url,调用MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取fi对应的所有权限,
         *           再调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够
         * @throws IOException
         * @throws ServletException
         */
        public void invoke(FilterInvocation fi) throws IOException, ServletException {
            InterceptorStatusToken token = super.beforeInvocation(fi);
            try {
                Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
                Collection<ConfigAttribute> attributes = myFilterInvocationSecurityMetadataSource.getAttributes(fi);
                HttpServletRequest request = fi.getHttpRequest();
                myAccessDecisionManager.decide(authentication, request, attributes);
                fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
            } finally {
                super.afterInvocation(token, null);
            }
        }
    
  • 第二点,上面调用了决策方法。myAccessDecisionManager.decide(authentication, request, attributes)
    决策方法也做了修改,具体改动,可以自己做对比。

        /**
     * 决策方法:权限判断
     *
     * @param authentication   用户的身份信息;
     * @param object           包含客户端发起的请求的request信息,可转换为 HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
     * @param configAttributes 是MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法返回的结果,
     *                         此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限;如果不在权限表中则放行。
     * @throws AccessDeniedException
     * @throws InsufficientAuthenticationException
     */
    @Override
    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
    
        if (CollectionUtils.isEmpty(configAttributes)) {
            throw new AccessDeniedException("没有权限");
        }
        for (GrantedAuthority ga : authentication.getAuthorities()) {
            if (configAttributes.contains(new SecurityConfig(ga.getAuthority()))) {
                return;
            }
        }
        throw new AccessDeniedException("没有权限");
    }
    

    这块逻辑,是看懂判断后,根据自己实现的UserDetailsService,里面的List<SimpleGrantedAuthority> authorities 数据结构来实现的。

  • 第三点,就是MyFilterInvocationSecurityMetadataSource.loadResourceDefine
    这里是加载所有的资源。逻辑:通过fi获取到用户请求的地址,然后通过请求的地址找到controller。 然而,controller 里面的地址都是在数据库的资源里面配置的。 在这里做了初始化。
    如果请求地址没有在这里匹配到,则全部被拦截。
    示例:
    在这里插入图片描述
    在这里插入图片描述

    /**
     * 加载资源,初始化资源变量
     */
    public void loadResourceDefine() {
        List<BasePrivilege> basePrivileges = allFeignServiceApi.baseUserFeignServiceApi.queryAllBasePrivilege();
        if(CollectionUtils.isNotEmpty(basePrivileges)){
            urlPermMap = new HashMap<>();
            basePrivileges.forEach(item->{
                if(StringUtils.isNotBlank(item.getPath())){
                    List<ConfigAttribute> authorityList = new ArrayList<>();
                    ConfigAttribute auth = new SecurityConfig(String.format("%s-%s", item.getResourceCode(), item.getCode()));
                    authorityList.add(auth);
                    urlPermMap.put(item.getPath(), authorityList);
                }
            });
        }
    }
    

结尾

实现步骤:先参考原文,把代码复制进去。然后参考上面的文章,添加urlPermMap后!自己根据自己的逻辑,慢慢一点一点添加,尽量不要使用我上面贴的代码,因为里面包含了自己的逻辑。
后面的再继续完善。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值