Spring Boot Shiro CAS集成

Spring Boot Shiro CAS集成

1 CAS Server搭建

1.1 获取CAS Server源码

GitHub地址 https://github.com/apereo/cas

1.2 去掉HTTPS协议

1.2.1 修改HTTPSandIMAPS-10000001.json中serviceId

​ 修改cas-server-webapp模块下services/HTTPSandIMAPS-10000001.json修改”serviceId”: 为”^(https|imaps|http)://.*”

​ 修改前:

{
  "@class": "org.jasig.cas.services.RegexRegisteredService",
  "serviceId": "^(https|imaps)://.*",
  "name": "HTTPS and IMAPS",
  "id": 10000001,
  "description": "This service definition authorized all application urls that support HTTPS and IMAPS protocols.",
  "proxyPolicy": {
    "@class": "org.jasig.cas.services.RefuseRegisteredServiceProxyPolicy"
  },
  ......
}

​ 修改后:

{
  "@class": "org.jasig.cas.services.RegexRegisteredService",
  "serviceId": "^(https|imaps|http)://.*",
  "name": "HTTPS and IMAPS",
  "id": 10000001,
  "description": "This service definition authorized all application urls that support HTTPS and IMAPS protocols.",
  "proxyPolicy": {
    "@class": "org.jasig.cas.services.RefuseRegisteredServiceProxyPolicy"
  },
 ......
}
1.2.2 修改一services/Apereo-10000002.json中serviceId

​ 修改cas-server-webapp模块下services/HTTPSandIMAPS-10000001.json修改”serviceId”: 为”^(https|imaps|http)://.*”

​ 修改前:

{
  "@class" : "org.jasig.cas.services.RegexRegisteredService",
  "serviceId" : "^(https|imaps)://.*",
  "name" : "Apereo",
  "theme" : "apereo",
  "id" : 10000002,
  "description" : "Apereo foundation sample service",
  "proxyPolicy" : {
    "@class" : "org.jasig.cas.services.RefuseRegisteredServiceProxyPolicy"
  },
 ......
}

​ 修改后:

{
  "@class" : "org.jasig.cas.services.RegexRegisteredService",
  "serviceId" : "^(https|imaps|http)://.*",
  "name" : "Apereo",
  "theme" : "apereo",
  "id" : 10000002,
  "description" : "Apereo foundation sample service",
  "proxyPolicy" : {
    "@class" : "org.jasig.cas.services.RefuseRegisteredServiceProxyPolicy"
  },
  ......
}
1.2.3 修改二ticketGrantingTicketCookieGenerator.xml中p:cookieSecure=”false”

​ 修改cas-server-webapp模块下WEB-INF/spring-configuration/ticketGrantingTicketCookieGenerator.xml

p:cookieSecure=”true”为false

​ 修改前:

<bean id="ticketGrantingTicketCookieGenerator" class="org.jasig.cas.web.support.CookieRetrievingCookieGenerator"
          c:casCookieValueManager-ref="cookieValueManager"
          p:cookieSecure="true"
          p:cookieMaxAge="-1"
          p:cookieName="TGC"
          p:cookiePath=""/>

​ 修改后:

<bean id="ticketGrantingTicketCookieGenerator" class="org.jasig.cas.web.support.CookieRetrievingCookieGenerator"
          c:casCookieValueManager-ref="cookieValueManager"
          p:cookieSecure="false"
          p:cookieMaxAge="-1"
          p:cookieName="TGC"
          p:cookiePath=""/>
1.2.4 修改warnCookieGenerator.xml中p:cookieSecure=”false”

​ 修改cas-server-webapp模块下WEB-INF\spring-configuration\warnCookieGenerator.xml

p:cookieSecure=”true”为false

​ 修改前:

<bean id="warnCookieGenerator" class="org.jasig.cas.web.support.CookieRetrievingCookieGenerator"
          p:cookieHttpOnly="true"
          p:cookieSecure="true"
          p:cookieMaxAge="-1"
          p:cookieName="CASPRIVACY"
          p:cookiePath=""/>

​ 修改后:

<bean id="warnCookieGenerator" class="org.jasig.cas.web.support.CookieRetrievingCookieGenerator"
          p:cookieHttpOnly="true"
          p:cookieSecure="false"
          p:cookieMaxAge="-1"
          p:cookieName="CASPRIVACY"
          p:cookiePath=""/>

1.3 连接数据库

1.3.1 添加jar包

​ 在cas-server-webapp pom.xml中添加jar

<dependency>
    <groupId>org.jasig.cas</groupId>
    <artifactId>cas-server-support-jdbc</artifactId>
    <version>4.1.10</version>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-collections4</artifactId>
    <version>4.1</version>
</dependency>
<dependency>
    <groupId>commons-dbcp</groupId>
    <artifactId>commons-dbcp</artifactId>
    <version>1.4</version>
</dependency>
1.3.2 修改配置文件deployerConfigContext.xml
<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:sec="http://www.springframework.org/schema/security"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
       http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
    <!--数据库配置开始-->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
        <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
        <property name="url" value="jdbc:oracle:thin:@192.168.2.251:1521:orcl"/>
        <property name="username" value="goisan_hunger"/>
        <property name="password" value="hunger"/>
    </bean>
    <!--数据库配置结束-->
    <!-- 配置登陆开始 -->
    <bean id="primaryAuthenticationHandler" class="org.jasig.cas.adaptors.jdbc.QueryDatabaseAuthenticationHandler">
        <property name="dataSource" ref="dataSource"/>
        <property name="sql" value="select password from T_SYS_USER where USER_ACCOUNT = ?"/>
        <property name="passwordEncoder" ref="MD5PasswordEncoder"/>
    </bean>
    <bean id="serviceRegistryDao" class="org.jasig.cas.services.InMemoryServiceRegistryDaoImpl">
        <property name="registeredServices">
            <list>
                <bean class="org.jasig.cas.services.RegexRegisteredService"
                      p:id="0" p:name="HTTP and IMAP" p:description="Allows HTTP(S) and IMAP(S) protocols"
                      p:serviceId="^(http?|https?|imaps?)://.*" p:evaluationOrder="10000001"/>
            </list>
        </property>
    </bean>
    <!-- 配置登陆结束 -->
    <!-- 返回用户信息 -->
    <bean id="attributeRepository" class="org.jasig.services.persondir.support.jdbc.SingleRowJdbcPersonAttributeDao">
        <constructor-arg index="0" ref="dataSource"/>
        <constructor-arg index="1" value="SELECT user_account,user_type FROM T_SYS_USER WHERE {0}"/>
        <property name="queryAttributeMapping">
            <map>
                <!--这里的key需写username,value对应数据库用户名字段-->
                <entry key="username" value="user_account"/>
            </map>
        </property>
        <property name="resultAttributeMapping">
            <map>
                <!--key为对应的数据库字段名称,value为提供给客户端获取的属性名字,系统会自动填充值 -->
                <entry key="user_account" value="user_account"/>
                <entry key="user_type" value="user_type"/>
            </map>
        </property>
    </bean>
    <!-- 返回用户信息结束 -->
    <!--配置md5加密-->
    <bean id="MD5PasswordEncoder" class="org.jasig.cas.authentication.handler.DefaultPasswordEncoder" autowire="byName">
        <constructor-arg value="MD5"/>
    </bean>
    <!--配置md5加密结束-->
    <bean id="authenticationManager" class="org.jasig.cas.authentication.PolicyBasedAuthenticationManager">
        <constructor-arg>
            <map>
                <entry key-ref="proxyAuthenticationHandler" value-ref="proxyPrincipalResolver"/>
                <entry key-ref="primaryAuthenticationHandler" value-ref="primaryPrincipalResolver"/>
            </map>
        </constructor-arg>
        <property name="authenticationPolicy">
            <bean class="org.jasig.cas.authentication.AnyAuthenticationPolicy"/>
        </property>
    </bean>
    <bean id="proxyAuthenticationHandler"
          class="org.jasig.cas.authentication.handler.support.HttpBasedServiceCredentialsAuthenticationHandler"
          p:httpClient-ref="supportsTrustStoreSslSocketFactoryHttpClient"/>
    <bean id="proxyPrincipalResolver"
          class="org.jasig.cas.authentication.principal.BasicPrincipalResolver"/>
    <bean id="primaryPrincipalResolver"
          class="org.jasig.cas.authentication.principal.PersonDirectoryPrincipalResolver"
          p:principalFactory-ref="principalFactory"
          p:attributeRepository-ref="attributeRepository"/>

    <bean id="auditTrailManager" class="org.jasig.inspektr.audit.support.Slf4jLoggingAuditTrailManager"/>

    <bean id="healthCheckMonitor" class="org.jasig.cas.monitor.HealthCheckMonitor" p:monitors-ref="monitorsList"/>

    <util:list id="monitorsList">
        <bean class="org.jasig.cas.monitor.MemoryMonitor" p:freeMemoryWarnThreshold="10"/>
        <bean class="org.jasig.cas.monitor.SessionMonitor"
              p:ticketRegistry-ref="ticketRegistry"
              p:serviceTicketCountWarnThreshold="5000"
              p:sessionCountWarnThreshold="100000"/>
    </util:list>
</beans>

2 Spring boot Shiro Cas集成

2.1 修改pom.xml

​ 在项目pom.xml中添加如下依赖

<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.4.0</version>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.4.0</version>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-cas</artifactId>
    <version>1.4.0</version>
    <!--Spring Boot 内嵌Tomcat不有servlet依赖,需要将其排除掉-->
    <exclusions>
        <exclusion>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
        </exclusion>
        <exclusion>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
</dependency>
<dependency>
    <groupId>org.opensaml</groupId>
    <artifactId>opensaml</artifactId>
    <version>1.1</version>
</dependency>
<dependency>
    <groupId>org.apache.santuario</groupId>
    <artifactId>xmlsec</artifactId>
    <version>1.4.3</version>
     <!--Spring Boot 内嵌Tomcat不有servlet依赖,需要将其排除掉-->
    <exclusions>
        <exclusion>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
        </exclusion>
        <exclusion>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
        </exclusion>
    </exclusions>
</dependency>

2.2 编写ShiroConfig

/**
 * 没集成cas我们会配置CredentialsMatcher,来做密码加密
 * 集成cas不可以配置CredentialsMatcher
 */
@Configuration
public class ShiroConfig {

    @Bean(name = "shiroFilterFactoryBean")
    public ShiroFilterFactoryBean shiroFilterFactoryBean(WebSecurityManager webSecurityManager,
                                                         @Value("${cas.server-url}") String casServerUrl,
                                                         @Value("${cas.service}") String casService) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(webSecurityManager);
        //拦截器.
        Map<String, Filter> filters = new HashMap<>();
        filters.put("cas", casFilter());
        shiroFilter.setFilters(filters);
        /**
         * 配置哪些页面需要受保护.
         * 以及访问这些页面需要的权限.
         * 1). anon 可以被匿名访问
         * 2). authc 必须认证(即登录)后才可能访问的页面.
         * 3). logout 登出.
         * 4). roles 角色过滤器
         * 5). cas 是指这个URL会被filters中的拦截器拦截
         */
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // 配置不会被拦截的链接 顺序判断
        filterChainDefinitionMap.put("/static/**", "anon");
        /**
         * 这个拦截路径要和casService的地址匹配
         */
        filterChainDefinitionMap.put("/cas", "cas");
        //配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
        filterChainDefinitionMap.put("/logout", "logout");
        //<!-- 过滤链定义,从上向下顺序执行,一般将/**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
        //<!-- authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问-->
        filterChainDefinitionMap.put("/**", "authc");
        // 如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
        shiroFilter.setLoginUrl(casServerUrl + "?service=" + casService + "/cas");
        // 登录成功后要跳转的链接
        shiroFilter.setSuccessUrl("/");
        //未授权界面;
        shiroFilter.setUnauthorizedUrl("/403");
        shiroFilter.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilter;
    }

    @Bean
    public WebSecurityManager webSecurityManager(Realm realm, RememberMeManager rememberMeManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm);
        securityManager.setRememberMeManager(rememberMeManager);
        return securityManager;
    }

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

    @Bean
    public CasFilter casFilter() {
        CasFilter casFilter = new CasFilter();
        casFilter.setSuccessUrl("/");
        casFilter.setFailureUrl("/403");
        return casFilter;
    }

    /**
     * 注解@DependsOn 让当前的bean在某个bean启动后启动
     */
    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    public Realm realm(@Value("${cas.server-url}") String casServerUrl,
                       @Value("${cas.service}") String casService) {
        LoginUserCasRealm realm = new LoginUserCasRealm();
        realm.setAuthenticationCachingEnabled(true);
        realm.setAuthenticationCacheName("authenticationCache");
        realm.setAuthorizationCacheName("authorizationCache");
        realm.setCasServerUrlPrefix(casServerUrl);
        realm.setCasService(casService + "/cas");
        return realm;
    }

    @Bean
    public Cookie cookie() {
        //这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
        SimpleCookie cookie = new SimpleCookie("JSID");
        //<!-- 记住我cookie生效时间30天 ,单位秒;-->
        cookie.setMaxAge(2592000);
        cookie.setHttpOnly(true);
        cookie.setSecure(true);
        return cookie;
    }

    @Bean
    public CookieRememberMeManager rememberMeManager(Cookie cookie) {
        CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
        rememberMeManager.setCookie(cookie);
        //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
        rememberMeManager.setCipherKey(Base64.decode("3AvVhmFLUs0KTA3Kprsdag=="));
        return rememberMeManager;
    }

    @Bean
    public AuthenticationStrategy authenticationStrategy() {
        return new AtLeastOneSuccessfulStrategy();
    }

    @Bean
    public Authenticator authenticator(AuthenticationStrategy authenticationStrategy) {
        ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator();
        authenticator.setAuthenticationStrategy(authenticationStrategy);
        return authenticator;
    }

    @Bean
    public SessionDAO sessionDAO(SessionIdGenerator sessionIdGenerator) {
        EnterpriseCacheSessionDAO sessionDAO = new EnterpriseCacheSessionDAO();
        sessionDAO.setActiveSessionsCacheName("shiro-activeSessionCache");
        sessionDAO.setSessionIdGenerator(sessionIdGenerator);
        return sessionDAO;
    }

    @Bean
    public SessionIdGenerator sessionIdGenerator() {
        return new JavaUuidSessionIdGenerator();
    }

    @Bean
    public SessionManager sessionManager(SessionDAO sessionDAO, Cookie cookie) {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setGlobalSessionTimeout(30 * 60 * 1000);
        sessionManager.setSessionDAO(sessionDAO);
        sessionManager.setSessionIdCookie(cookie);
        return sessionManager;
    }
}

2.3 编写Realm

public class XxxRealm extends CasRealm {
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        Set<String> strings = new HashSet<>();
        strings.add("USER");
        //这里获取用户角色和权限的逻辑,每个项目不一样,所以没写实现代码
        //设置用户拥有的角色
        authorizationInfo.setRoles(strings);
        //设置用户拥有的权限
        authorizationInfo.setStringPermissions(strings);
        return authorizationInfo;
    }
}

2.4 配置application.properties

#cas地址,外网访问需用外网IP
cas.server-url=http://localhost
#当前项目地址,外网访问需用外网IP
cas.service=http://localhost:8080

猜你喜欢

转载自blog.csdn.net/lht931942788/article/details/80091559