Cas整合Shiro

1.簡介

CAS:Yale 大學發起的一個開源項目,旨在爲 Web 應用系統提供一種可靠的單點登錄方法。

Shiro:Apache Shiro是一個Java安全框架,可以幫助我們完成認證、授權、會話管理、加密等,並且提供與web集成、緩存、rememberMed等功能。

Shiro支持與CAS進行整合使用.

2.整合步驟

2.1.新建工程

在這裏插入圖片描述

2.2.導入項目中需要的依賴

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.bruce.cas.shiro.demo</groupId>
    <artifactId>CasShiro</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!--導入SpringBoot的依賴-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.4.RELEASE</version>
        <relativePath/>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <shiro.version>1.2.4</shiro.version>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>


        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>${shiro.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>${shiro.version}</version>
        </dependency>


        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-cas</artifactId>
            <version>${shiro.version}</version>
        </dependency>

        <dependency>
            <groupId>net.mingsoft</groupId>
            <artifactId>shiro-freemarker-tags</artifactId>
            <version>0.1</version>
        </dependency>

        <!--導入thymeleaf支持shiro的標籤-->
        <dependency>
            <groupId>com.github.theborakompanioni</groupId>
            <artifactId>thymeleaf-extras-shiro</artifactId>
            <version>2.0.0</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>

2.3.啓動類

package com.bruce;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * @BelongsProject: CasShiro
 * @BelongsPackage: com.bruce
 * @Author: bruceliu
 * @QQ:1241488705
 * @CreateTime: 2020-04-10 23:08
 * @Description: TODO
 */
@SpringBootApplication
public class App {

    public static void main(String[] args) {
        SpringApplication.run(App.class,args);
    }
}

2.4.配置文件

server.port=8888

#Thymeleaf配置
spring.thymeleaf.cache=false
spring.thymeleaf.encoding=utf-8
#設置爲LEGACYHTML5編碼格式
spring.thymeleaf.mode=LEGACYHTML5
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html

cas.casServerUrlPrefix=http://127.0.0.1:8080
cas.casServerLoginUrl=${cas.casServerUrlPrefix}/login
#cas.casServerLoginUrl=http://127.0.0.1:8081/shiroCAS/toLogin
cas.casServerLogoutUrl=${cas.casServerUrlPrefix}/logout?service=http://www.baidu.com
cas.casFilterUrlPattern=/shiro-cas
cas.localServerUrlPrefix=http://localhost:${server.port}
cas.localServerLoginUrl=${cas.casServerLoginUrl}?service=${cas.localServerUrlPrefix}${cas.casFilterUrlPattern}

2.5.加載CAS的配置文件

在這裏插入圖片描述

package com.bruce.config;

/**
 * @BelongsProject: CasShiro
 * @BelongsPackage: com.bruce.config
 * @Author: bruceliu
 * @QQ:1241488705
 * @CreateTime: 2020-04-17 22:45
 * @Description: TODO
 */
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.context.properties.ConfigurationProperties;

/**
 * 這個類是shiro整合cas的時候需要的一些配置
 * 申明的一些變量
 */
@SpringBootConfiguration
@ConfigurationProperties(prefix = "cas")
public class CasConfig {

    private String casServerUrlPrefix = "https://server.fable.com:8443/cas";
    private String casServerLoginUrl = casServerUrlPrefix + "/login";
    private String casServerLogoutUrl = casServerUrlPrefix + "/logout";
    private String localServerUrlPrefix = "http://client1.fable.com:8081/aaaa";
    private String casFilterUrlPattern = "/shiro-cas";
    private String localServerLoginUrl = casServerLoginUrl + "?service=" + localServerUrlPrefix + casFilterUrlPattern;

    public String getCasServerUrlPrefix() {
        return casServerUrlPrefix;
    }

    public void setCasServerUrlPrefix(String casServerUrlPrefix) {
        this.casServerUrlPrefix = casServerUrlPrefix;
    }

    public String getCasServerLoginUrl() {
        return casServerLoginUrl;
    }

    public void setCasServerLoginUrl(String casServerLoginUrl) {
        this.casServerLoginUrl = casServerLoginUrl;
    }

    public String getCasServerLogoutUrl() {
        return casServerLogoutUrl;
    }

    public void setCasServerLogoutUrl(String casServerLogoutUrl) {
        this.casServerLogoutUrl = casServerLogoutUrl;
    }

    public String getLocalServerUrlPrefix() {
        return localServerUrlPrefix;
    }

    public void setLocalServerUrlPrefix(String localServerUrlPrefix) {
        this.localServerUrlPrefix = localServerUrlPrefix;
    }

    public String getCasFilterUrlPattern() {
        return casFilterUrlPattern;
    }

    public void setCasFilterUrlPattern(String casFilterUrlPattern) {
        this.casFilterUrlPattern = casFilterUrlPattern;
    }

    public String getLocalServerLoginUrl() {
        return localServerLoginUrl;
    }

    public void setLocalServerLoginUrl(String localServerLoginUrl) {
        this.localServerLoginUrl = localServerLoginUrl;
    }

    public String getCasService() {
        return localServerUrlPrefix + casFilterUrlPattern;
    }
}


2.6.配置Realm

package com.bruce.shiro;

import com.bruce.pojo.User;
import com.bruce.service.IUserService;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.cas.CasRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.HashSet;
import java.util.Set;


/**
 * shiro和cas整合之後 繼承CasRealm
 */
public class MyShiroCasRealm extends CasRealm{

    @Autowired
    private IUserService userService;

    /**
     * 權限認證(爲當前登錄的Subject授予角色和權限)
     *
     * 該方法的調用時機爲需授權資源被訪問時,並且每次訪問需授權資源都會執行該方法中的邏輯,這表明本例中並未啓用AuthorizationCache,
     * 如果連續訪問同一個URL(比如刷新),該方法不會被重複調用,Shiro有一個時間間隔(也就是cache時間,在ehcache-shiro.xml中配置),
     * 超過這個時間間隔再刷新頁面,該方法會被執行
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String loginName = (String) super.getAvailablePrincipal(principals);
        User user = userService.findByUsername(loginName);
        if (user != null){
            
            // 權限信息對象info,用來存放查出的用戶的所有的角色及權限
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            //下面就是查詢用戶的權限
            Set<String> roles=new HashSet<String>();
            roles.add("admin");
            roles.add("normal");
            //下面也就可以查詢用戶的角色
            Set<String> perms=new HashSet<String>();
            perms.add("del");
            perms.add("query");
            perms.add("user:query");

            info.setRoles(roles);
            info.setStringPermissions(perms);

            System.out.println("這裏是授權的地方..............................");
            return info;
        }
        // 返回null將會導致用戶訪問任何被攔截的請求時都會自動跳轉到unauthorizedUrl指定的地址
        return null;
    }
}

2.7.配置ShiroFilterFactoryBean

package com.bruce.shiro;

import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.mgt.FilterChainManager;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.web.mgt.WebSecurityManager;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.springframework.beans.factory.BeanInitializationException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

/**
 * 繼承ShiroFilterFactoryBean處理攔截資源文件問題
 */
public class MyShiroFilterFactoryBean extends ShiroFilterFactoryBean {
    // ShiroFilter將直接忽略的請求
    private Set<String> ignoreExt;

    public MyShiroFilterFactoryBean() {
        super();
        ignoreExt = new HashSet<String>();
        ignoreExt.add(".jpg");
        ignoreExt.add(".png");
        ignoreExt.add(".gif");
        ignoreExt.add(".bmp");
        ignoreExt.add(".js");
        ignoreExt.add(".css");
    }

    @Override
    protected AbstractShiroFilter createInstance() throws Exception {
        SecurityManager securityManager = getSecurityManager();
        if (securityManager == null) {
            throw new BeanInitializationException("SecurityManager property must be set.");
        }

        if (!(securityManager instanceof WebSecurityManager)) {
            throw new BeanInitializationException("The security manager does not implement the WebSecurityManager interface.");
        }

        PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
        FilterChainManager chainManager = createFilterChainManager();
        chainResolver.setFilterChainManager(chainManager);
        return new MySpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
    }

    private class MySpringShiroFilter extends AbstractShiroFilter {
        public MySpringShiroFilter(
                WebSecurityManager securityManager, PathMatchingFilterChainResolver chainResolver) {
            super();
            if (securityManager == null) {
                throw new IllegalArgumentException("WebSecurityManager property cannot be null.");
            }
            setSecurityManager(securityManager);
            if (chainResolver != null) {
                setFilterChainResolver(chainResolver);
            }
        }

        @Override
        protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse,
                                        FilterChain chain)
                throws ServletException, IOException {
            HttpServletRequest request = (HttpServletRequest) servletRequest;
            String str = request.getRequestURI().toLowerCase();
            boolean flag = true;
            int idx = 0;
            if ((idx = str.indexOf(".")) > 0) {
                str = str.substring(idx);
                if (ignoreExt.contains(str.toLowerCase())) {
                    flag = false;
                }
            }
            if (flag) {
                super.doFilterInternal(servletRequest, servletResponse, chain);
            } else {
                chain.doFilter(servletRequest, servletResponse);
            }
        }
    }
}

2.8. Shiro集成CAS配置

package com.bruce.shiro;

import com.bruce.config.CasConfig;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.cas.CasFilter;
import org.apache.shiro.cas.CasSubjectFactory;
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.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.DelegatingFilterProxy;

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

/**
 * Shiro集成CAS配置
 */
@Configuration
@EnableConfigurationProperties(CasConfig.class)
public class ShiroCasConfiguration {

    private static final String CAS_FILTER = "casFilter";

    @Bean
    public EhCacheManager ehcacheManager(){
        EhCacheManager ehcacheManager = new EhCacheManager();
        ehcacheManager.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");
        return ehcacheManager;
    }

    @Bean(name = "myShiroCasRealm")
    public MyShiroCasRealm myShiroRealm(EhCacheManager ehCacheManager, CasConfig casConfig){
        MyShiroCasRealm realm = new MyShiroCasRealm();
        realm.setCacheManager(ehCacheManager);
        realm.setCasServerUrlPrefix(casConfig.getCasServerUrlPrefix());
        realm.setCasService(casConfig.getCasService());
        return realm;
    }

    /**
     * 註冊shiroFilter
     */
    @Bean
    public FilterRegistrationBean filterRegistrationBean(){
        FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
        filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter"));
        // 該值缺省爲false,表示生命週期有SpringApplicationContext管理,設置爲true則表示由ServletContainer管理
        filterRegistration.addInitParameter("targetFilterLifecycle", "true");
        filterRegistration.setEnabled(true);
        filterRegistration.addUrlPatterns("/*");
        return filterRegistration;
    }

    @Bean(name = "lifecycleBeanPostProcessor")
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
        return new LifecycleBeanPostProcessor();
    }

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

    @Bean(name = "securityManager")
    public DefaultWebSecurityManager defaultWebSecurityManager(MyShiroCasRealm realm){
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm);
        securityManager.setCacheManager(ehcacheManager());
        // 指定SubjectFactory
        securityManager.setSubjectFactory(new CasSubjectFactory());
        return securityManager;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }

    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager, CasConfig casConfig, CasFilter casFilter){
        ShiroFilterFactoryBean factoryBean = new MyShiroFilterFactoryBean();
        factoryBean.setSecurityManager(securityManager);
        factoryBean.setLoginUrl(casConfig.getLocalServerLoginUrl());
        factoryBean.setSuccessUrl("/user");
        factoryBean.setUnauthorizedUrl("/403");
        // 添加casFilter到shiroFilter中
        Map<String, Filter> filterMap = new HashMap<String, Filter>(1);
        filterMap.put(CAS_FILTER, casFilter);
        factoryBean.setFilters(filterMap);
        loadShiroFilterChain(factoryBean, casConfig);
        return factoryBean;
    }

    /**
     * 加載ShiroFilter權限控制規則
     */
    private void loadShiroFilterChain(ShiroFilterFactoryBean factoryBean, CasConfig casConfig) {
        /**下面這些規則配置最好配置到配置文件中*/
        Map<String, String> filterChainMap = new LinkedHashMap<String, String>();
        filterChainMap.put(casConfig.getCasFilterUrlPattern(), CAS_FILTER);//shiro集成cas後,首先添加該規則
        filterChainMap.put("/main", "anon");
        filterChainMap.put("/**", "authc");
        factoryBean.setFilterChainDefinitionMap(filterChainMap);
    }

    /**
     * CAS過濾器
     */
    @Bean
    public CasFilter casFilter(CasConfig casConfig){
        CasFilter casFilter = new CasFilter();
        casFilter.setName(CAS_FILTER);
        casFilter.setEnabled(true);
        casFilter.setFailureUrl(casConfig.getLocalServerLoginUrl());
        return casFilter;
    }

}

2.9.集成Shiro標籤

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;

/**
 * 集成Shiro標籤
 */
@SpringBootConfiguration
public class ShiroTagThymeleafConfigurer{
    /**
     * 配置的是方言
     * @return
     */
    @Bean
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }
}

2.10.頁面使用

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:th="http://www.thymeleaf.org"
      xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
    <meta charset="UTF-8"/>
    <title>Title</title>
</head>
<body>
<h1>用戶列表-<a href="logout">登出</a></h1>
<h2>權限列表</h2>
<shiro:authenticated>用戶已經登錄顯示此內容</shiro:authenticated><br/>
<shiro:hasRole name="manager">manager角色登錄顯示此內容</shiro:hasRole><br/>
<shiro:hasRole name="admin">admin角色登錄顯示此內容</shiro:hasRole><br/>
<shiro:hasRole name="normal">normal角色登錄顯示此內容</shiro:hasRole><br/>
<shiro:hasAnyRoles name="manager,admin">manager或admin角色用戶登錄顯示此內容</shiro:hasAnyRoles><br/>
<shiro:principal>-顯示當前登錄用戶名-</shiro:principal><br/>
<shiro:hasPermission name="add">add權限用戶顯示此內容</shiro:hasPermission><br/>
<br/>所有用戶列表:<br/>

<ul th:each="user1,iterStat: ${userList}">
    <li th:text="${user1.userName}"></li>
</ul>

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