spring boot shiro 采坑指南

遇到的一个问题:ShiroFilterFactoryBean 配置完成后,该filter不起作用。

原因:在项目中还定义了一个别的DotDOFilter(该Filter的作用是把Url中xxx.do结尾Url修改成xxx),
          在这个DotDOFilter中使用了request.getRequestDispatcher(uri).forward(request, response);

          在这个DotDOFilter之后的ShiroFilter就不起作用了。

注意:Filter可以指定DispatcherType的类型,详见https://blog.csdn.net/xiaokang123456kao/article/details/72885171

处理方式:把ShiroFilter放在Filter前面即可.

@Bean
public FilterRegistrationBean securityFilterChain(AbstractShiroFilter securityFilter) {
    FilterRegistrationBean registration = new FilterRegistrationBean(securityFilter);
    registration.setOrder(Integer.MAX_VALUE-1);//排在DotDOFilter的前面
    registration.setName("shiroFilter");
    //registration.setDispatcherTypes(DispatcherType.REQUEST,DispatcherType.FORWARD);
    registration.setDispatcherTypes(DispatcherType.REQUEST);
    return registration;

}


查看Filter顺序的方法:


参考:

1)filter中的dispatcher标签解析  https://blog.csdn.net/xiaokang123456kao/article/details/72885171

2)Shiro和AJAX完美整合   https://blog.csdn.net/wu560130911/article/details/11819761

3)springboot(十四):springboot整合shiro-登录认证和权限管理  https://blog.csdn.net/ityouknow/article/details/73836159

4)Springboot与shiro整合遇到的坑  https://zhuanlan.zhihu.com/p/29161098

代码:

1)ShiroConfig.java

package com.alf.test.platform.config;

import com.alf.test.platform.shiro.*;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.apache.shiro.mgt.SecurityManager;
import org.springframework.context.annotation.DependsOn;

import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {
    /**
     * 配置Url过滤器
     * @param securityManager
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
        System.out.println("ShiroConfiguration.shirFilter()");
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();//获取filters
        filters.put("authc", new CustomFormAuthenticationFilter());//注入自定义的FormAuthenticationFilter
        filters.put("logout", new CustomLogoutFilter());//注入自定义的LogoutFilter

        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //拦截器.
        Map<String,String> filterChainDefinitionMap = new LinkedHashMap<String,String>();
        // 配置不会被拦截的链接 顺序判断
        //filterChainDefinitionMap.put("/abctest/test1", "myFilter1");
        filterChainDefinitionMap.put("/static/**", "anon");

        filterChainDefinitionMap.put("/swagger-ui.html", "anon");
        filterChainDefinitionMap.put("/swagger-resources", "anon");
        filterChainDefinitionMap.put("/v2/api-docs", "anon");
        filterChainDefinitionMap.put("/webjars/springfox-swagger-ui/**", "anon");

//        filterChainDefinitionMap.put("/login", "anon");
//        filterChainDefinitionMap.put("/logout", "anon");
        //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
        filterChainDefinitionMap.put("/logout", "logout");
        filterChainDefinitionMap.put("/logout.do", "logout");
        //<!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
        //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
       // filterChainDefinitionMap.put("/**", "myFilter1,authc");
        filterChainDefinitionMap.put("/**", "authc");
        shiroFilterFactoryBean.setLoginUrl("/login");
        // 登录成功后要跳转的链接
        //shiroFilterFactoryBean.setSuccessUrl("/index");

        //未授权界面;
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
//        Map<String, Filter> map = new HashMap<>();
//        map.put("myFilter1", new MyOncePerRequestFilter());
//        shiroFilterFactoryBean.setFilters(map);
        return shiroFilterFactoryBean;
    }

    @Bean
    public FilterRegistrationBean securityFilterChain(AbstractShiroFilter securityFilter) {
        FilterRegistrationBean registration = new FilterRegistrationBean(securityFilter);
        registration.setOrder(Integer.MAX_VALUE-1);//排在ZHandleDotDOFilter的前面
        registration.setName("shiroFilter");
        //registration.setDispatcherTypes(DispatcherType.REQUEST,DispatcherType.FORWARD);
        registration.setDispatcherTypes(DispatcherType.REQUEST);
        return registration;
    }

    /**
     * Shiro生命周期处理器
     * @return
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
        return new LifecycleBeanPostProcessor();
    }
    /**
     * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
     * @return
     */
    @Bean
    @DependsOn({"lifecycleBeanPostProcessor"})
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * 注入Realm
     * @return
     */
    @Bean
    public QdasShiroRealm qdasShiroRealm(){
        QdasShiroRealm qdasShiroRealm = new QdasShiroRealm();
        return qdasShiroRealm;
    }


    @Bean
    public SecurityManager securityManager(){
        DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
        securityManager.setRealm(qdasShiroRealm());
       securityManager.setSessionManager(configWebSessionManager());
        //securityManager.setCacheManager();
        return securityManager;
    }

    @Bean
    public SessionManager configWebSessionManager(){
        DefaultWebSessionManager manager = new DefaultWebSessionManager();
        //manager.setCacheManager(cacheManager);// 加入缓存管理器
        //manager.setSessionDAO(sessionDao);// 设置SessionDao
        manager.setDeleteInvalidSessions(true);// 删除过期的session
        manager.setGlobalSessionTimeout(1800000);// 设置全局session超时时间
        manager.setSessionValidationSchedulerEnabled(true);// 是否定时检查session
        manager.setSessionIdCookie(simpleCookie());
        return manager;
    }

    /**
     * 注入cookie模板
     * @return
     */
    @Bean
    public SimpleCookie simpleCookie(){
        SimpleCookie simpleCookie = new SimpleCookie("sid-shrio");
        simpleCookie.setMaxAge(-1);
        simpleCookie.setHttpOnly(true);
        return simpleCookie;
    }
}

2)CustomFormAuthenticationFilter.java

package com.alf.test.platform.shiro;

import com.alibaba.fastjson.JSONObject;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;

/**
 * 自定义FormAuthenticationFilter:
 * 1 修改用户名和密码的获取方式,使用sid
 * 2 定义了登陆的Url
 * 3 修改了登陆成功和失败的处理方式
 */
public class CustomFormAuthenticationFilter extends FormAuthenticationFilter {

    private static final Logger logger = LoggerFactory.getLogger(CustomFormAuthenticationFilter.class);

    public CustomFormAuthenticationFilter(){
        super();
    }

    @Override
    protected String getUsername(ServletRequest request) {
        try {
            JSONObject jsonObject = getJsonObject(request);
            return jsonObject.getString("sid");
        } catch (IOException e) {
            e.printStackTrace();
        }

        return super.getUsername(request);
    }

    public static JSONObject getJsonObject(ServletRequest request) throws IOException{
        String resultStr = "";
        String readLine;
        StringBuffer sb = new StringBuffer();
        BufferedReader responseReader = null;
        OutputStream outputStream = null;
        try {
            responseReader = new BufferedReader(new InputStreamReader(request.getInputStream(), "UTF-8"));
            while ((readLine = responseReader.readLine()) != null) {
                sb.append(readLine).append("\n");
            }
            //responseReader.close();
            resultStr = sb.toString();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (outputStream != null) {
                //outputStream.close();
            }
        }
        return JSONObject.parseObject(resultStr);
    }

    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        // 在这里进行验证码的校验
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpSession session = httpServletRequest.getSession();

        return super.onAccessDenied(request, response);
    }

    /**
     * 目前项目中登陆URL为:/login.do, /login
     * @param request
     * @param response
     * @return
     */
    @Override
    protected boolean isLoginRequest(ServletRequest request, ServletResponse response) {
        String requestURI = getPathWithinApplication(request);
        if ("/login.do".equals(requestURI) || "/login".equals(requestURI)){
            return true;
        }
        return false;
    }

    /**
     * 目前使用XXXsid登陆, 不需要密码
     * @param request
     * @return
     */
    @Override
    protected String getPassword(ServletRequest request) {
        return "";
    }

    /**
     * 登陆成功, 直接执行接下来的流程
     * @param token
     * @param subject
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject,
                                     ServletRequest request, ServletResponse response) throws Exception {
        logger.info("登陆成功!");
        return true;
    }

    /**
     * 重写该方法, 使用Dispatcher, 转发到指定controller
     * @param request
     * @param response
     * @throws IOException
     */
    protected void saveRequestAndRedirectToLogin(ServletRequest request, ServletResponse response) throws IOException {
        try {
            request.getRequestDispatcher("/noLogin").forward(request, response);
        } catch (ServletException e) {
            e.printStackTrace();
        }
    }
}

3)CustomLogoutFilter.java

package com.alf.test.platform.shiro;

import org.apache.shiro.session.SessionException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.util.Locale;

public class CustomLogoutFilter extends LogoutFilter {
    private static final Logger logger = LoggerFactory.getLogger(CustomLogoutFilter.class);
    @Override
    protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {

        Subject subject = getSubject(request, response);

        // Check if POST only logout is enabled
        if (isPostOnlyLogout()) {

            // check if the current request's method is a POST, if not redirect
            if (!WebUtils.toHttp(request).getMethod().toUpperCase(Locale.ENGLISH).equals("POST")) {
                return onLogoutRequestNotAPost(request, response);
            }
        }

        //String redirectUrl = getRedirectUrl(request, response, subject);
        //try/catch added for SHIRO-298:
//        try {
//            subject.logout();
//        } catch (SessionException ise) {
//            logger.debug("Encountered session exception during logout.  This can generally safely be ignored.", ise);
//        }
        //issueRedirect(request, response, redirectUrl);
        return true;
    }

    @Override
    public void afterCompletion(ServletRequest request, ServletResponse response, Exception exception) throws Exception {
        Subject subject = getSubject(request, response);
        try {
            subject.logout();
        } catch (SessionException ise) {
            logger.debug("Encountered session exception during logout.  This can generally safely be ignored.", ise);
        }
    }
}

4)DotDOFilter.java

package com.alf.test.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.web.filter.RequestContextFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 处理aaa.do访问aaa的问题
 */
@Configuration
@WebFilter(urlPatterns = "/")
public class DotDOFilter extends RequestContextFilter {
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String uri = request.getRequestURI();
        if (uri.endsWith(".do")) {
            uri = uri.substring(0, uri.length() - 3);
            request.getRequestDispatcher(uri).forward(request, response);
        } else {
            super.doFilterInternal(request, response, filterChain);
        }
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值