SpringBoot集成Shiro实现单点登录

570 阅读3分钟

公司使用的安全架构是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);
    }

}

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