jeesite集成cas认证[通俗易懂]

jeesite集成cas认证[通俗易懂]cas和shiro集成,很好的解决了登录及权限问题。本人最近第一次使用,框架使用的是jeesite开源框架,本身已经集成了shiro,现在将cas集成到项目中。折腾了三天,终于把cas集成到jeesite中。现将集成过程写下,供朋友参考。本项目集成cas的同时还留有登录入口,此时需要多种认证方式,步骤6、7的设置就是针对这个功能的,如不需要可直接跳过。1、添加cas的maven依赖。

大家好,又见面了,我是你们的朋友全栈君。

cas和shiro集成,很好的解决了登录及权限问题。本人最近第一次使用,框架使用的是jeesite开源框架,本身已经集成了shiro,现在将cas集成到项目中。

折腾了三天,终于把cas集成到jeesite中。现将集成过程写下,供朋友参考。

本项目集成cas的同时还留有登录入口,此时需要多种认证方式,步骤6、7的设置就是针对这个功能的,如不需要可直接跳过。

不做技术好多年了,项目时间紧只能亲自上阵,写的不周全的请多包涵。有问题望指教。

1、添加cas的maven依赖。

  <!-- CAS -->
  <dependency>
      <groupId>org.jasig.cas.client</groupId>
      <artifactId>cas-client-core</artifactId>
      <version>3.2.1</version>
  </dependency>

2、web.xml添加内容。

<!– 该过滤器对HttpServletRequest请求包装, 可通过HttpServletRequest的getRemoteUser()方法获得登录用户的登录名,可选 –>   
   

 <filter>    
        <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>    
        <filter-class>    
            org.jasig.cas.client.util.HttpServletRequestWrapperFilter    
        </filter-class>    
    </filter>    
    <filter-mapping>    
        <filter-name>CAS HttpServletRequest Wrapper Filter</filter-name>    
        <url-pattern>/*</url-pattern>    
    </filter-mapping>    
    <!-- 该过滤器使得可以通过org.jasig.cas.client.util.AssertionHolder来获取用户的登录名。    
         比如AssertionHolder.getAssertion().getPrincipal().getName()。     
         这个类把Assertion信息放在ThreadLocal变量中,这样应用程序不在web层也能够获取到当前登录信息 -->    
    <filter>    
        <filter-name>CAS Assertion Thread Local Filter</filter-name>    
        <filter-class>org.jasig.cas.client.util.AssertionThreadLocalFilter</filter-class>   
    </filter>    
    <filter-mapping>    
        <filter-name>CAS Assertion Thread Local Filter</filter-name>    
        <url-pattern>/*</url-pattern>    
    </filter-mapping> 

   

3、配置spring-content-shiro.xml。

整体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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
  http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.1.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd"
 default-lazy-init="true">
 <description>Shiro Configuration</description>
    <!-- 加载配置属性文件 -->
 <context:property-placeholder ignore-unresolvable="true" location="classpath:project.properties" />
 
 <!-- Shiro权限过滤过滤器定义 -->
 <bean name="shiroFilterChainDefinitions" class="java.lang.String">
  <constructor-arg>
   <value>    
    /static/** = anon
                /userfiles/** = anon
                ${adminPath}/login = authc
                ${adminPath}/logout = logout
                ${adminPath}/cas = cas
                ${adminPath}/** = user
   </value>
  </constructor-arg>
 </bean>
 
 <bean id="logout" class="org.apache.shiro.web.filter.authc.LogoutFilter">
          <property name="redirectUrl" 
        value="${cas.logout.url}"/>
 </bean>
 
 <!-- 安全认证过滤器 -->
 <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
  <property name="securityManager" ref="securityManager" /> 
  <property name="loginUrl" value="${cas.server.url}/login?service=${cas.project.url}${adminPath}/cas" />
  <!--<property name="loginUrl" value="${adminPath}/login" /> -->
  <property name="successUrl" value="${adminPath}" />
  <property name="filters">
            <map>
                <entry key="cas" value-ref="casFilter"/>
                <entry key="authc" value-ref="formAuthenticationFilter"/>
                <entry key="logout" value-ref="logout" />
            </map>
        </property>
  <property name="filterChainDefinitions">
   <ref bean="shiroFilterChainDefinitions"/>
  </property>
 </bean>
 
 <!-- cas和shiro结合  -->
 <bean id="casRealm" class="com.sinosoft.modules.sys.security.CasLoginRealm">
     <property name="casServerUrlPrefix" value="${cas.server.url}"></property>
     <property name="casService" value="${cas.project.url}${adminPath}/cas"></property>
   </bean>
   <!-- CAS认证过滤器 -->  
 <bean id="casFilter" class="org.apache.shiro.cas.CasFilter">  
  <property name="failureUrl" value="${adminPath}/login"/>
 </bean>
 <!-- 定义Shiro安全管理配置 -->
 <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
  <property name="realms">
   <list>
    <ref bean="casRealm"/>
    <ref bean="systemAuthorizingRealm"/>
   </list>
  </property>
  <property name="sessionManager" ref="sessionManager" />
  <property name="cacheManager" ref="shiroCacheManager" />
 </bean>
 
 <!-- 配置使用自定义认证器,可以实现多Realm认证,并且可以指定特定Realm处理特定类型的验证 -->
    <bean id="authenticator" class="com.sinosoft.modules.sys.security.CustomizedModularRealmAuthenticator">
        <!-- 配置认证策略,只要有一个Realm认证成功即可,并且返回所有认证成功信息 -->
        <property name="authenticationStrategy">
            <bean class="org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy"></bean>
        </property>
    </bean>
 
 <!-- 自定义会话管理配置 -->
 <bean id="sessionManager" class="com.sinosoft.common.security.shiro.session.SessionManager">
  <property name="sessionDAO" ref="sessionDAO"/>
  
  <!-- 会话超时时间,单位:毫秒  -->
  <property name="globalSessionTimeout" value="${session.sessionTimeout}"/>
  
  <!-- 定时清理失效会话, 清理用户直接关闭浏览器造成的孤立会话   -->
  <property name="sessionValidationInterval" value="${session.sessionTimeoutClean}"/>
<!--    <property name="sessionValidationSchedulerEnabled" value="false"/> -->
   <property name="sessionValidationSchedulerEnabled" value="true"/>
   
  <property name="sessionIdCookie" ref="sessionIdCookie"/>
  <property name="sessionIdCookieEnabled" value="true"/>
 </bean>
 
 <!-- 指定本系统SESSIONID, 默认为: JSESSIONID 问题: 与SERVLET容器名冲突, 如JETTY, TOMCAT 等默认JSESSIONID,
  当跳出SHIRO SERVLET时如ERROR-PAGE容器会为JSESSIONID重新分配值导致登录会话丢失! -->
 <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
     <constructor-arg name="name" value="jeesite.session.id"/>
 </bean>
 <!-- 自定义Session存储容器 -->
<!--  <bean id="sessionDAO" class="com.sinosoft.common.security.shiro.session.JedisSessionDAO"> -->
<!--   <property name="sessionIdGenerator" ref="idGen" /> -->
<!--   <property name="sessionKeyPrefix" value="${redis.keyPrefix}_session_" /> -->
<!--  </bean> -->
 <bean id="sessionDAO" class="com.sinosoft.common.security.shiro.session.CacheSessionDAO">
  <property name="sessionIdGenerator" ref="idGen" />
  <property name="activeSessionsCacheName" value="activeSessionsCache" />
  <property name="cacheManager" ref="shiroCacheManager" />
 </bean>
 
 <!-- 自定义系统缓存管理器-->
<!--  <bean id="shiroCacheManager" class="com.sinosoft.common.security.shiro.cache.JedisCacheManager"> -->
<!--   <property name="cacheKeyPrefix" value="${redis.keyPrefix}_cache_" /> -->
<!--  </bean> -->
 <bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
  <property name="cacheManager" ref="cacheManager"/>
 </bean>
 
 <!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
 <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
 
 <!-- AOP式方法级权限检查  -->
 <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
  <property name="proxyTargetClass" value="true" />
 </bean>
 <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
     <property name="securityManager" ref="securityManager"/>
 </bean>
 
</beans>

4、project.properties中增加属性url。

cas.server.url=http://localhost:8080/cas
cas.project.url=http://1ocalhost:8080/infoService
cas.logout.url=http://localhost:8080/cas/logout?server=http://localhost:8080/infoService

5、创建CasLoginRealm类,继承CasRealm。

package com.sinosoft.modules.sys.security;
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.sinosoft.common.config.Global;  
import com.sinosoft.common.utils.SpringContextHolder;  
import com.sinosoft.common.web.Servlets;  
import com.sinosoft.modules.sys.entity.Menu;  
import com.sinosoft.modules.sys.entity.Role;  
import com.sinosoft.modules.sys.entity.User;  
import com.sinosoft.modules.sys.security.SystemAuthorizingRealm.Principal;  
import com.sinosoft.modules.sys.service.SystemService;  
import com.sinosoft.modules.sys.utils.LogUtils;  
import com.sinosoft.modules.sys.utils.UserUtils;  
/**
 * <p>Title: cas认证类</p>
 * <p>Description: cas认证操作类</p>
 * <p>Copyright: Copyright (c) 2017</p>
 * <p>Company: </p>
 * <p>Date: 2017-7-5</p>
 * @author holyspirit
 * @version 1.0
 */
public class CasLoginRealm 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);
   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) {
   logger.error("票据认证失败", 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;
  }
 }
    /**
     * 
     * Title:获取系统业务对象
     * Description:获取系统业务对象
     * @param 
     * @return 
     * @Author:holyspirit
     * @Create Date: 2017-7-6
     * @Modifier:
     * @Modify Date:
     */
 public SystemService getSystemService() {
  if (systemService == null) {
   systemService = SpringContextHolder.getBean(SystemService.class);
  }
  return systemService;
 }
}

6、FormAuthenticationFilter类增加代码片段,重写redirectToLogin方法。

@Override
 protected void redirectToLogin(ServletRequest request,
   ServletResponse response) throws IOException {
  setLoginUrl("/info/login");
  WebUtils.issueRedirect(request, response, getLoginUrl());
 }

7、新增CustomizedModularRealmAuthenticator类。

package com.sinosoft.modules.sys.security;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import java.util.Collection;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.cas.CasToken;
import org.apache.shiro.realm.Realm;
/**
 * <p>Title: 自定义Authenticator</p>
 * <p>Description:分别定义处理不同登录方式验证的Realm</p>
 * <p>Copyright: Copyright (c) 2017</p>
 * <p>Company: </p>
 * <p>Date: 2017-7-6</p>
 * @author holyspirit
 * @version 1.0
 */
public class CustomizedModularRealmAuthenticator extends
  ModularRealmAuthenticator {
 @Override
 protected AuthenticationInfo doAuthenticate(
   AuthenticationToken authenticationToken)
   throws AuthenticationException {
  //获取所有Realm
  Collection<Realm> realms = getRealms();
  Realm realm = null;
  //判断当前token是castoken还是UsernamePasswordToken
  //强转对应类型token
  //根据token类型获得对应的Realm
  if(authenticationToken instanceof CasToken){
   authenticationToken = (CasToken)authenticationToken;
   realm = getRealm(realms, authenticationToken);
  }
  if(authenticationToken instanceof UsernamePasswordToken){
   authenticationToken = (UsernamePasswordToken)authenticationToken;
   realm = getRealm(realms, authenticationToken);
  }
  //执行响应Realm
  return doSingleRealmAuthentication(realm, authenticationToken);
 }
 
 //判断当前token对应的Realm
 public Realm getRealm(Collection<Realm> realms, AuthenticationToken token){
  for (Realm realm : realms) {
   if(realm.supports(token)){
    return realm;
   }
  }
  return null;
 }
}

8、设置注销事件,退出cas认证同时清除系统session

<a href="${ctx}/logout" title="退出登录">退出</a>

问题集合:
1。中文乱码问题,所有提交的请求中文变成乱码,这是过滤器顺序所致。cas过滤器和shiro过滤器要放在编码过滤器后面。


版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/148373.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长

相关推荐

  • 自己动手用Android和Xposed编写一个微信聊天机器人——《微信聊天精灵》实现关键词自动回复。

    自己动手用Android和Xposed编写一个微信聊天机器人——《微信聊天精灵》实现关键词自动回复。出于爱好和需要,想着自己来编写一个微信聊天机器人,能实现以下功能:能实时获取到微信聊天消息; 能进行文本自动回复; 能够设置关键词; 能够根据关键词匹配,进行内容回复; 能实现聊天消息云端备份; 已回复的消息能自动标记已读; ……待定 能够作为一个产品,让用户直接下载和安装使用。 开源。可以到github上去下载源码,和release安装包。 免费。不收费。咱也用现在…

    2022年5月29日
  • UCOSII操作系统 第3课—UCOSII启动过程

    UCOSII操作系统 第3课—UCOSII启动过程1、初始化UCOSII(1)在调用UCOSII在任何的其他的服务之前,UCOSII要求首先调用初始化函数OSInit();这个函数的目的就是在整个系统启动之前,初始化所有的变量和数据结构。(2)其中,在OSInit()函数中建立空闲任务OS_TaskIdle();这个任务总是处于就绪态的,空闲任务的优先级是设置为最低的。(3)调用OSInit以后,任务控制块缓冲池中有OS_MAX_TASKS个任务控制块,事件控制缓冲区中有OS_MAX_EVENTS个事件控制块,消息队列缓冲池OS_Q中有OS_MAX

    2022年5月29日
  • 架构设计基础:单服务.集群.分布式,基本区别和联系

    架构设计基础:单服务.集群.分布式,基本区别和联系

    2020年11月20日
  • java流程图平行四边形_流程图图形标准含义[通俗易懂]

    rectangle:矩形作用:一般用作要执行的处理(process),在程序流程图中做执行框。在Axure中如果是画页面框架图,那么也可以指代一个页面。你可把页面和执行命令放在同一个流程中做说明,这个时候将两类不同的矩形做色彩区别,然后做说明就好了。roundedrectangle:圆角矩形或者扁圆作用:表示程序的开始或者结束,在程序流程图中用作为起始框或者结束框。beveledrectang…

    2022年4月9日
  • Unity3d快速入门[通俗易懂]

    Unity3d快速入门[通俗易懂]https://www.zhihu.com/question/313621072Unity3d如何快速入门前言进入一个领域,最直接有效的方法就是,寻找相关综述性文章,首先你需要对你入门的领域有个概括性的了解,这些包括:1、主流的学习社区与网站。2、该领域的知名大牛与热心分享的从业者。3、如何有效的激励自己持续学习—主要是动手实践因此,总结一下Unity相关学习资源,可能有些不足,欢迎大家指正修改,一起成长!授人与鱼,不如授人与渔!一、Unity官方学习资源1、【Unity官.

    2022年8月10日
  • iphone手机获取不了软件_手机uuid什么意思

    iphone手机获取不了软件_手机uuid什么意思UDID是一种iOS设备的特殊识别码。除序号之外,每台iOS装置都另有一组独一无二的号码,我们就称之为识别码(UniqueDeviceIdentifier,UDID)。每个iOS开发者在缴交99美元年费的同时,都会得到100个betatest名额,可以让软件开发者输入100台iOS装置的UDID。有意成为betatester的人,只需向开发者提交UDID,经过输入及制成一个描述

    2022年8月9日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号