Apache shiro—cas + shiro配置说明

shiro在1.2版本之后加入了对cas的支持

SSO的概念

    单点登录( Single Sign-On , 简称 SSO )是目前比较流行的服务于企业业务整合的解决方案之一, SSO就是一次登录,就可以访问多个互相信任的应用。而cas只是这种解决方案的一种实现。
    这么说吧:大家应该都去过游乐场,通常会卖通票。就是买一张票,就可以玩游乐场中的所有项目,而不用玩一项买一项的票。这就是单点登录最直白的理解了:“通票”。

原理:

图里的票据指的是,第三步用户的验证信息通过验证后,cas服务器随机生成的一个唯一的身份标识。这个票据会被存储在浏览器的cookie中。注:这里只是直白的说法,后面会有博客详细这部分的过程。
    下图,表明的是一个web应用的登录过程,这时候用户访问第二个应用时可以直接拿浏览器中的cookie去cas认证,因为应用1已经证过了。因此用户访问第二个应用时就不需要重新登陆了。

Spring-shiro.xml的配置:

    上面大概的说了一下,cas的验证原理。下面就看看cas和shiro集成的配置。这个是基于前一篇博客的,如果不记得可以翻看前一篇博客。一下配置都已本地为例:
    首先,引入Jar包。shiro-cas-1.2.3.jar、cas-client-core-3.2.0.jar

    cas.server.url=http://125.223.29.88:8087/cas
    cas.project.url=http://127.0.0.1:8889/PipeRackMonitor

    然后,在spring - shiro.xml修改如下配置:

  • 第一:shiroSecurityFilter修改为:
    <bean name="shiroFilterChainDefinitions" class="java.lang.String">
    		<constructor-arg>
    			<value>
    				/static/** = anon
    				/userfiles/** = anon
    				${adminPath}/cas = cas
    				${adminPath}/login = authc
    				${adminPath}/logout = logout
    				${adminPath}/** = user
    				/act/editor/** = user
    				/ReportServer/** = user
    			</value>
    		</constructor-arg>
    	</bean>
    	 
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    		<property name="securityManager" ref="securityManager" />
    	 	<property name="loginUrl" value="${cas.server.url}?service=${cas.project.url}${adminPath}/cas" /> 
    		<property name="successUrl" value="${adminPath}?login" />
    		<property name="filters">
                <map>
                    <entry key="cas" value-ref="casFilter"/>
                    <entry key="authc" value-ref="formAuthenticationFilter"/>
                </map>
            </property>
    		<property name="filterChainDefinitions">
    			<ref bean="shiroFilterChainDefinitions"/>
    		</property>
    </bean>

     

  • 第二:shiroRealm的配置中添加两个属性,分别为cas服务器的登录地址和cas客户端的入口即会被拦截的地址:
    <bean id="casRealm" class="com.thinkgem.jeesite.modules.sys.security.MyCasRealm">
            <property name="casServerUrlPrefix" value="${cas.server.url}"/>
            客户端的回调地址设置,必须和下面的shiro-cas过滤器拦截的地址一致 
            <property name="casService" value="${cas.project.url}${adminPath}/cas"/>
    </bean>

    第三:创建MyCasRealm:

    import java.util.Collection;
    import java.util.List;
    import org.apache.commons.lang3.StringUtils;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authc.SimpleAuthenticationInfo;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.cas.CasAuthenticationException;
    import org.apache.shiro.cas.CasRealm;
    import org.apache.shiro.cas.CasToken;
    import org.apache.shiro.session.Session;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.apache.shiro.subject.SimplePrincipalCollection;
    import org.jasig.cas.client.authentication.AttributePrincipal;
    import org.jasig.cas.client.validation.Assertion;
    import org.jasig.cas.client.validation.TicketValidationException;
    import org.jasig.cas.client.validation.TicketValidator;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import com.thinkgem.jeesite.common.config.Global;
    import com.thinkgem.jeesite.common.utils.SpringContextHolder;
    import com.thinkgem.jeesite.common.web.Servlets;
    import com.thinkgem.jeesite.modules.sys.entity.Menu;
    import com.thinkgem.jeesite.modules.sys.entity.Role;
    import com.thinkgem.jeesite.modules.sys.entity.User;
    import com.thinkgem.jeesite.modules.sys.security.SystemAuthorizingRealm.Principal;
    import com.thinkgem.jeesite.modules.sys.service.SystemService;
    import com.thinkgem.jeesite.modules.sys.utils.LogUtils;
    import com.thinkgem.jeesite.modules.sys.utils.UserUtils;
    public class MyCasRealm extends CasRealm{
    	 private Logger logger = LoggerFactory.getLogger(getClass());
    	    private SystemService systemService;
    
    	    @Override
    	    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    //	      return super.doGetAuthenticationInfo(token);
    	        CasToken casToken = (CasToken) token;
    	        if (token == null) {
    	            return null;
    	        }
    	        //获取ticket
    	        String ticket = (String)casToken.getCredentials();
    	        if (!org.apache.shiro.util.StringUtils.hasText(ticket)) {
    	            return null;
    	        }
    
    	        TicketValidator ticketValidator = ensureTicketValidator();
    
    	        try {
    	            //回传ticket到服务端验证,验证通过就进入下一行,可以获取登录后的相关信息,否则直接抛异常,即验证不通过
    	            Assertion casAssertion = ticketValidator.validate(ticket, getCasService());
    	            AttributePrincipal casPrincipal = casAssertion.getPrincipal();
    	            String userId = casPrincipal.getName();
    	            User user = getSystemService().getUserByLoginName(userId.split("&&")[1]);
    	            if (user != null) {
    	                Principal p = new  Principal(user, false);
    	                PrincipalCollection principalCollection = new SimplePrincipalCollection(p, getName());
    	                return new SimpleAuthenticationInfo(principalCollection, ticket);
    	            } else {
    	                return null;
    	            }
    
    	        } catch (TicketValidationException e) { 
    	            throw new CasAuthenticationException("Unable to validate ticket [" + ticket + "]", e);
    	        }
    
    	    }
    
    	    @Override
    	    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    	        Principal principal = (Principal) getAvailablePrincipal(principals);
    	        // 获取当前已登录的用户
    	        if (!Global.TRUE.equals(Global.getConfig("user.multiAccountLogin"))){
    	            Collection<Session> sessions = getSystemService().getSessionDao().getActiveSessions(true, principal, UserUtils.getSession());
    	            if (sessions.size() > 0){
    	                // 如果是登录进来的,则踢出已在线用户
    	                if (UserUtils.getSubject().isAuthenticated()){
    	                    for (Session session : sessions){
    	                        getSystemService().getSessionDao().delete(session);
    	                    }
    	                }
    	                // 记住我进来的,并且当前用户已登录,则退出当前用户提示信息。
    	                else{
    	                    UserUtils.getSubject().logout();
    	                    throw new AuthenticationException("msg:账号已在其它地方登录,请重新登录。");
    	                }
    	            }
    	        }
    	        User user = getSystemService().getUserByLoginName(principal.getLoginName());
    	        if (user != null) {
    	            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    	            List<Menu> list = UserUtils.getMenuList();
    	            for (Menu menu : list){
    	                if (StringUtils.isNotBlank(menu.getPermission())){
    	                    // 添加基于Permission的权限信息
    	                    for (String permission : StringUtils.split(menu.getPermission(),",")){
    	                        info.addStringPermission(permission);
    	                    }
    	                }
    	            }
    	            // 添加用户权限
    	            info.addStringPermission("user");
    	            // 添加用户角色信息
    	            for (Role role : user.getRoleList()){
    	                info.addRole(role.getEnname());
    	            }
    	            // 更新登录IP和时间
    	            getSystemService().updateUserLoginInfo(user);
    	            // 记录登录日志
    	            LogUtils.saveLog(Servlets.getRequest(), "系统登录");
    	            return info;
    	        } else {
    	            return null;
    	        }
    	    }
    
    	    /**
    	     * 获取系统业务对象
    	     */
    	    public SystemService getSystemService() {
    	        if (systemService == null){
    	            systemService = SpringContextHolder.getBean(SystemService.class);
    	        }
    	        return systemService;
    	    }
    }

    接着:Web.xml的配置,加入多点登出的配置,这里主要是从cas服务器上清除用户的登录信息:

    <listener>
    	<listener-class>org.jasig.cas.client.session.SingleSignOutHttpSessionListener</listener-class>
    </listener>
    <filter>
    	<filter-name>singleSignOutFilter</filter-name>
    	<filter-class>org.jasig.cas.client.session.SingleSignOutFilter</filter-class>
    </filter>
    <filter-mapping>
    	<filter-name>singleSignOutFilter</filter-name>
    	<url-pattern>/*</url-pattern>
    </filter-mapping>

    退出的请求地址应该为:cas服务器注销地址+web应用退出地址作为参数。

    
    <a href="${cas.server.url}/logout?service=${cas.project.url}/logout">退出</a>

     

 

转载于:https://my.oschina.net/u/3621003/blog/1490212

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值