第三部分:shiro整合spring使用cas單點登入配置__cas

來源:互聯網
上載者:User
第三部分 shiro整合spring使用cas單點登入配置 (一)shiro單點登入

  配置的主要目的在於將登入頁面改為${cas.server}?service=${cas.client}/login的形式,service後面為本地的回調地址。在cas伺服器端登入成功後,會產生ticket返回給用戶端,用戶端的shiro使用ticket最為憑據儲存起來。
  
  shiro配置單點登陸後,在登出時原始的cas-client只能刪除HttpSession,不能刪除shiro的Session,因此未使用shiro的session管理器。
  如果想啟用shiro的Session管理器,可以參考Shiro & CAS 實現單點登入。
  原有的CasRealm在AuthenticationInfo中只儲存了使用者名稱作為principal,MyCasRealm中重寫了此方法,改為儲存使用者資訊類。
  需要在pom增加依賴,shiro-cas會通過依賴傳遞自動增加cas-client-core-3.2.1的依賴。

<!-- shiro --><dependency>    <groupId>org.apache.shiro</groupId>    <artifactId>shiro-spring</artifactId>    <version>1.2.3</version></dependency><dependency>    <groupId>org.apache.shiro</groupId>    <artifactId>shiro-cas</artifactId>    <version>1.2.3</version></dependency>
1、spring-shiro.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:util="http://www.springframework.org/schema/util"    xmlns:aop="http://www.springframework.org/schema/aop"     xmlns:context="http://www.springframework.org/schema/context"    xsi:schemaLocation="    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd    http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd    " default-lazy-init="false">    <description>Shiro安全配置</description>    <!-- 緩衝管理器 -->    <bean id="cacheManager" class="com.whty.framework.base.common.cache.SpringCacheManagerWrapper">        <property name="cacheManager" ref="springCacheManager"/>    </bean>    <!-- Realm實現 -->    <bean id="casRealm" class="com.whty.oim.base.shiro.MyCasRealm">        <property name="cachingEnabled" value="true"/>        <property name="authenticationCachingEnabled" value="true"/>        <property name="authenticationCacheName" value="authenticationCache"/>        <property name="authorizationCachingEnabled" value="true"/>        <property name="authorizationCacheName" value="authorizationCache"/>        <!-- CAS Server -->        <property name="casServerUrlPrefix" value="${cas.server}"/>        <!-- 用戶端的回調地址設定,必須和下面的shiro-cas過濾器攔截的地址一致 -->        <property name="casService" value="${cas.client}/login"/>    </bean>    <bean id="casSubjectFactory" class="org.apache.shiro.cas.CasSubjectFactory"/>    <!-- 安全管理器 -->    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">        <property name="realm" ref="casRealm"/>        <!-- <property name="sessionManager" ref="sessionManager"/> -->        <property name="cacheManager" ref="cacheManager"/>        <!-- sessionMode參數設定為native時,那麼shrio就將使用者的基本認證資訊儲存到預設名稱為shiro-activeSessionCache 的Cache中 -->        <!--<property name="sessionMode" value="native" />-->        <property name="subjectFactory" ref="casSubjectFactory"/>    </bean>    <!-- 相當於調用SecurityUtils.setSecurityManager(securityManager) -->    <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">        <property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>        <property name="arguments" ref="securityManager"/>    </bean>    <bean id="casFilter" class="org.apache.shiro.cas.CasFilter">        <!-- 配置驗證錯誤時的失敗頁面  -->        <property name="failureUrl" value="${cas.client}"/>    </bean>    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">        <property name="securityManager" ref="securityManager" />        <!-- 設定角色的登入連結,這裡為cas登入頁面的連結可配置回調地址  -->        <property name="loginUrl" value="${cas.server}?service=${cas.client}/login" />        <property name="successUrl" value="/index" />        <property name="unauthorizedUrl" value="/"/>         <property name="filters">            <util:map>                <!-- <entry key="authc" value-ref="authcFilter"/>                <entry key="captchaFilter" value-ref="captchaFilter"/> -->                <!-- 添加casFilter到shiroFilter -->                <entry key="cas" value-ref="casFilter"/>            </util:map>        </property>        <property name="filterChainDefinitions">            <value>            /logout = logout            /login = cas            /** = user            </value>        </property>    </bean>    <!--保證實現了Shiro內部lifecycle函數的bean執行 -->    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" /></beans>
2、cas.properties

  cas.server=http://192.168.5.129:8080/cas
  cas.client=http://192.168.4.184:8091/ucs 3、自訂Realm實現

  主要修改了CasRealm的doGetAuthenticationInfo()方法,CasRealm預設只儲存了username,本系統改為儲存ShiroUser對象。

public class MyCasRealm extends CasRealm {    Logger logger = LoggerFactory.getLogger(MyCasRealm.class);    @Autowired    private UcsUserService ucsUserService;    @Autowired    private UcsRoleService ucsRoleService;    @Autowired    private UcsPermissionService ucsPermissionService;    @Value("${domain}")    private String domain;    /**     * 授權查詢回呼函數, 進行鑒權但緩衝中無使用者的授權資訊時調用.     *      * @see org.apache.shiro.realm.AuthorizingRealm#doGetAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection)     */    @Override    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {        ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal();        UcsUser ucsUser = ucsUserService.selectByUsername(shiroUser.loginName);        //把principals放session中 key=userId value=principals        SecurityUtils.getSubject().getSession().setAttribute(String.valueOf(ucsUser.getId()),SecurityUtils.getSubject().getPrincipals());        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();        //賦予角色        List<UcsRole> ucsRoles = ucsRoleService.selectByUser(ucsUser.getId());        if (!CheckEmptyUtil.isEmpty(ucsRoles)){            for(UcsRole ucsRole:ucsRoles){                info.addRole(ucsRole.getId());            }        }        //賦予許可權//        List<UcsPermission> ucsPermissions = ucsPermissionService.selectByUser(ucsUser.getId(), domain);        if (!CheckEmptyUtil.isEmpty(shiroUser.getMenus())){            for(EasyUIMenu permission:shiroUser.getMenus()){                if(UcsBaseConstant.PermissionType.BUTTON.equals(permission.getType()))                info.addStringPermission(permission.getUrl());            }        }        return info;    }    /**     * Authenticates a user and retrieves its information.     *      * @param token the authentication token     * @throws AuthenticationException if there is an error during authentication.     */    @Override    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {        CasToken casToken = (CasToken) token;        if (token == null) {            return null;        }        String ticket = (String)casToken.getCredentials();        if (!StringUtils.hasText(ticket)) {            return null;        }        TicketValidator ticketValidator = ensureTicketValidator();        try {            // contact CAS server to validate service ticket            Assertion casAssertion = ticketValidator.validate(ticket, getCasService());            // get principal, user id and attributes            AttributePrincipal casPrincipal = casAssertion.getPrincipal();            String username = casPrincipal.getName();            logger.debug("Validate ticket : {} in CAS server : {} to retrieve user : {}", new Object[]{                    ticket, getCasServerUrlPrefix(), username            });            Map<String, Object> attributes = casPrincipal.getAttributes();            // refresh authentication token (user id + remember me)            casToken.setUserId(username);            String rememberMeAttributeName = getRememberMeAttributeName();            String rememberMeStringValue = (String)attributes.get(rememberMeAttributeName);            boolean isRemembered = rememberMeStringValue != null && Boolean.parseBoolean(rememberMeStringValue);            if (isRemembered) {                casToken.setRememberMe(true);            }            // create simple authentication info            // 根據使用者名稱擷取帳號資訊            UcsUser ucsUser = ucsUserService.selectByUsername(username);            List<UcsPermission> ucsPermissions = null;            if (ucsUser != null) {                ucsPermissions = ucsPermissionService.selectByUser(ucsUser.getId(), domain);            } else {                throw new UnknownAccountException();//登入失敗            }            //給菜單排序            List<EasyUIMenu> menus = toEasyUIMenu(ucsPermissions);            if(!CheckEmptyUtil.isEmpty(menus)){                Collections.sort(menus);            }            ShiroUser shiroUser = new ShiroUser(ucsUser.getId(), ucsUser.getUsername(), ucsUser.getName(), menus);            return new SimpleAuthenticationInfo(shiroUser, ticket, getName());        } catch (TicketValidationException e) {             throw new CasAuthenticationException("Unable to validate ticket [" + ticket + "]", e);        }    }    private List<EasyUIMenu> toEasyUIMenu(List<UcsPermission> ucsPermissions){        List<EasyUIMenu> menus = new ArrayList<EasyUIMenu>(0);        for (UcsPermission ucsPermission : ucsPermissions) {            menus.add(toEasyUIMenu(ucsPermission));        }        return menus;    }    private EasyUIMenu toEasyUIMenu(UcsPermission ucsPermission){        return new EasyUIMenu(ucsPermission.getId(), ucsPermission.getPid(), ucsPermission.getDomain(), ucsPermission.getName(), ucsPermission.getType(), ucsPermission.getSort(), ucsPermission.getIcon(), ucsPermission.getUrl(), ucsPermission.getDescription(), ucsPermission.getStatus());    }    @Override    public void clearCachedAuthorizationInfo(PrincipalCollection principals) {        super.clearCachedAuthorizationInfo(principals);    }    @Override    public void clearCachedAuthenticationInfo(PrincipalCollection principals) {        super.clearCachedAuthenticationInfo(principals);    }    @Override    public void clearCache(PrincipalCollection principals) {        super.clearCache(principals);        clearAllCache();    }    public void clearAllCachedAuthorizationInfo() {        getAuthorizationCache().clear();    }    public void clearAllCachedAuthenticationInfo() {        getAuthenticationCache().clear();    }    public void clearAllCache() {        clearAllCachedAuthenticationInfo();        clearAllCachedAuthorizationInfo();    }}
(二)cas登出

  
  在任意一個子系統登出後,cas伺服器會向所有發過登入請求的子系統發送一個登出請求,使所有子系統的HttpSession失效。如果使用了shiro的session管理器,需要修改cas用戶端代碼,在移除HttpSession的時候也移除shiro的Session。 1、web.xml

  web.xml中需要加入cas的SingleSignOutFilter實現單點登出功能,該過濾器需要放在shiroFilter之前,spring字元集過濾器之後。在實際使用時發現,SingleSignOutFilter如果放在了spring字元集過濾器之前,資料在傳輸過程中就會出現亂碼。

<!-- 用於單點退出,該過濾器用於實現單點登出功能,可選配置。--><listener>    <listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class></listener><!-- 該過濾器用於實現單點登出功能,可選配置。 --><filter>    <filter-name>CAS Single Sign Out Filter</filter-name>    <filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class></filter><filter-mapping>    <filter-name>CAS Single Sign Out Filter</filter-name>    <url-pattern>/*</url-pattern></filter-mapping>
2、登出請求

  登出只需要將當前頁面跳轉到cas伺服器的logout頁面即可實現登出。如果需要登出後跳轉頁面,可以加入service參數,並參考第一部分中第四節進行配置。
  子系統使用的方法是在後台拼裝好登出連結,發送給前台頁面,前台頁面接受使用el運算式接收。具體實現需要根據每個子系統的不同來自行實現。

@Value("${cas.server}")private String cas_server;  @Value("${cas.client}")private String cas_client;@RequestMapping(value = "/index")public String index(Model model){    StringBuilder logoutUrl = new StringBuilder();    logoutUrl.append(cas_server);    logoutUrl.append("/logout?service=");    logoutUrl.append(cas_client);    model.addAttribute("logoutUrl", logoutUrl.toString());    return "system/index";}

  前台頁面:

<script>    var logoutUrl = '${logoutUrl}';</script>
緩衝配置(可選)

我使用的ehcache作為shiro的緩衝,也可以使用其他的。
spring-cache.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:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"    xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx"    xsi:schemaLocation="    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd    http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"    default-lazy-init="false">    <bean id="springCacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">        <property name="cacheManager" ref="ehcacheManager"/>    </bean>    <!--ehcache-->    <bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">        <property name="configLocation" value="classpath:properties/ehcache.xml"/>    </bean></beans>

ehcache.xml

<?xml version="1.0" encoding="UTF-8"?>  <ehcache xmlns:xsi="http://ww

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.