springboot shiro 前後分離 前端請求302

前後端分離項目中,由於跨域,會導致複雜請求,即會發送preflighted request,這樣會導致在GET/POST等請求之前會先發一個OPTIONS請求,但OPTIONS請求並不帶shiro的’Authorization’字段(shiro的Session),即OPTIONS請求不能通過shiro驗證,會返回未認證的信息。
解決方案爲繼承shiro的DefaultWebSessionManager類重寫獲取session的方法

public class MySessionManager extends DefaultWebSessionManager {

    private static final String AUTHORIZATION = "Authorization";

    private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";

    public MySessionManager() {
        super();
    }

    @Override
    protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
        String id = WebUtils.toHttp(request).getHeader(AUTHORIZATION);
        //如果請求頭中有 Authorization 則其值爲sessionId
        if (!StrKit.isEmpty(id)) {
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
            request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
            return id;
        } else {
            //否則按默認規則從cookie取sessionId
            return super.getSessionId(request, response);
        }
    }

}

然後在shiro配置類中將我們自定義的session管理類註冊進去

public class ShiroConfiguration {

    @Autowired
    private IResourceService resourceService;


    @Bean("lifecycleBeanPostProcessor")
    public static LifecycleBeanPostProcessor getLifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * ShiroFilterFactoryBean 處理攔截資源文件問題。
     * 注意:單獨一個ShiroFilterFactoryBean配置是或報錯的,因爲在
     * 初始化ShiroFilterFactoryBean的時候需要注入:SecurityManager
     * <p>
     * Filter Chain定義說明
     * 1、一個URL可以配置多個Filter,使用逗號分隔
     * 2、當設置多個過濾器時,全部驗證通過,才視爲通過
     * 3、部分過濾器可指定參數,如perms,roles
     */
    @Bean
    public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        // 必須設置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 如果不設置默認會自動尋找Web工程根目錄下的"/login.jsp"頁面
        Map<String, Filter> filters = new HashMap<String, Filter>();
        filters.put("admin", new AdminFormAuthenticationFilter());
        shiroFilterFactoryBean.setFilters(filters);

        //攔截器.
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
        //配置退出 過濾器,其中的具體的退出代碼Shiro已經替我們實現了
        filterChainDefinitionMap.put("/assets/**", "anon");
        filterChainDefinitionMap.put("/**/favicon.ico", "anon");
        filterChainDefinitionMap.put("/logout", "logout");
        filterChainDefinitionMap.put("/druid/**", "anon");
        filterChainDefinitionMap.put("/websocket/**", "anon");
        filterChainDefinitionMap.put("/swagger-ui.html", "anon");
        filterChainDefinitionMap.put("/swagger-resources/**", "anon");
        filterChainDefinitionMap.put("/v2/api-docs", "anon");
        filterChainDefinitionMap.put("/webjars/**", "anon");
        filterChainDefinitionMap.put("/**", "admin");

        List<Resource> menuList = resourceService.selectAll();
        for (Resource menu : menuList) {
            if (!StringUtils.isEmpty(menu.getUrl()) && !StringUtils.isEmpty(menu.getCode())) {
                String permission = "authc,perms[" + menu.getCode() + "]";
                filterChainDefinitionMap.put(menu.getUrl(), permission);
            }
        }
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }


    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        Map<String, Realm> shiroAuthenticatorRealms = new HashMap<>();
        shiroAuthenticatorRealms.put(LoginType.ADMIN.getType(), dBShiroRealm());

        CustomModularRealmAuthenticator customModularRealmAuthenticator = new CustomModularRealmAuthenticator();
        customModularRealmAuthenticator.setDefinedRealms(shiroAuthenticatorRealms);
        customModularRealmAuthenticator.setAuthenticationStrategy(authenticationStrategy());
        securityManager.setAuthenticator(customModularRealmAuthenticator);


        ModularRealmAuthorizer customModularRealmAuthorizer = new ModularRealmAuthorizer();
        customModularRealmAuthorizer.setRealms(shiroAuthenticatorRealms.values());
        securityManager.setAuthorizer(customModularRealmAuthorizer);
        securityManager.setSessionManager(sessionManager());

        securityManager.setCacheManager(shiroRedisCacheManager());
        return securityManager;
    }

    @Bean
    public DBShiroRealm dBShiroRealm() {
        DBShiroRealm dbShiroRealm = new DBShiroRealm();
        dbShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        dbShiroRealm.setCacheManager(shiroRedisCacheManager());
        dbShiroRealm.setCachingEnabled(true);
        return dbShiroRealm;
    }

    /**
     * Shiro默認提供了三種 AuthenticationStrategy 實現:
     * AtLeastOneSuccessfulStrategy :其中一個通過則成功。
     * FirstSuccessfulStrategy :其中一個通過則成功,但只返回第一個通過的Realm提供的驗證信息。
     * AllSuccessfulStrategy :凡是配置到應用中的Realm都必須全部通過。
     * authenticationStrategy
     *
     * @return
     */
    @Bean(name = "authenticationStrategy")
    public AuthenticationStrategy authenticationStrategy() {
        System.out.println("ShiroConfiguration.authenticationStrategy()");
        return new FirstSuccessfulStrategy();
    }

    /**
     * 憑證匹配器
     * (由於我們的密碼校驗交給Shiro的SimpleAuthenticationInfo進行處理了
     * 所以我們需要修改下doGetAuthenticationInfo中的代碼;
     * )
     *
     * @return
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        //散列算法:這裏使用MD5算法;
        hashedCredentialsMatcher.setHashAlgorithmName("md5");
        //散列的次數,比如散列兩次,相當於 md5(md5(""));
        hashedCredentialsMatcher.setHashIterations(2);
        return hashedCredentialsMatcher;
    }


    /**
     * 開啓shiro aop註解支持.
     * 使用代理方式;所以需要開啓代碼支持;
     *
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }


    @Bean
    @ConditionalOnMissingBean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }

    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public ShiroRedisCacheManager shiroRedisCacheManager() {
        return new ShiroRedisCacheManager();
    }


    /**
     * shiro session的管理
     */
    @Bean("sessionManager")
    public MySessionManager sessionManager() {
        MySessionManager sessionManager = new MySessionManager();
        sessionManager.setGlobalSessionTimeout(1800000 * 2);
        return sessionManager;
    }


}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章