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)
上一篇 2022年6月15日 下午8:00
下一篇 2022年6月15日 下午8:16


相关推荐

  • webform激活成功教程及汉化

    webform激活成功教程及汉化首先现在官网上下载最新版本 webstorm 需要的可以私信我 自己最好下最新的 安装直接下一步就可以了 安装完成后会弹出一个注册框 先不要管也不要关闭 打开下载的激活成功教程包 根据你的电脑系统运行相应的服务器模拟程序 32 位系统的 xp win7 win10 运行 dvt jb licsrv 386 exe64 位系统的 win10 amd 运行 dvt jb licsrv amd64 exe

    2026年3月18日
    2
  • 小红书知识卡片制作智能体以及工作流详解,扣子最新教程

    小红书知识卡片制作智能体以及工作流详解,扣子最新教程

    2026年3月12日
    3
  • clientWidth、offsetWidth、区别「建议收藏」

    clientWidth、offsetWidth、区别「建议收藏」clientWidth和clientHeigh、clientTop和clientLeft1,clientWidth的实际宽度clientWidth=width+左右padding2,clientHeigh的实际高度clientHeigh=height+上下padding3,clientTop的实际宽度clientTop=boder.top(上边框的宽度)4,clientLeft的实际宽度

    2022年7月22日
    21
  • vue2使用animate css[通俗易懂]

    vue2使用animate css[通俗易懂]vue2使用animatecss先上几个链接vue插件大集合:awesome-vuevue2插件:vue2-animate:vue2-animatevue2插件vue2-animateDEMO:vue2-animatedemo:vue2-animate-demo我想用过animatecss的都知道这是一个极其简单而又酷炫的css动画库,但是我想在vue2中使用anima

    2022年7月12日
    23
  • sg90舵机工作原理_舵机跑舵?锲而不舍终除隐患![通俗易懂]

    sg90舵机工作原理_舵机跑舵?锲而不舍终除隐患![通俗易懂]因为专注才更专业,快快关注吧!欢迎投稿:sea@shipmg.com正文开始一、前言某司二条姐妹船采用SSP5型360º全回转电力推进器,左右两台中压永磁电机推进船舶行驶,另外两面分别由两台P14S金杯油泵驱动三台CA210型赫格隆内曲线液压油马达360º控制船舶回转方向。在机动航行时使用港口模式,推进器可以360º全回转,正常航行时使用海上模式,推进器只能在左右35º…

    2022年6月21日
    38
  • PyCharm Flask框架安装

    PyCharm Flask框架安装打开命名行窗口 执行下面命令 pipinstallfl pipinstallfl login pipinstallfl openid pipinstallfl sqlalchemy pipinstallsq migrate pipinstallfl whooshalche

    2025年8月31日
    4

发表回复

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

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