springmvc + shiro整合 cas(记录一下)

一:在pom文件中引入依赖

        <dependency>
            <groupId>net.unicon.cas</groupId>
            <artifactId>cas-client-autoconfig-support</artifactId>
            <version>2.0.0-GA</version>
        </dependency>
        <dependency>
            <groupId>org.jasig.cas.client</groupId>
            <artifactId>cas-client-core</artifactId>
            <version>3.4.1</version>
        </dependency>
        <dependency>
           <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-cas</artifactId>
            <version>${shiro.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

二:在properties配置文件中配置cas服务端访问地址

cas.server.url=http://www.casserver.com:8443
cas.project.url=http://127.0.0.1:8088

三:修改spring-context-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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
		http://www.springframework.org/schema/context  http://www.springframework.org/schema/context/spring-context-4.0.xsd"
	default-lazy-init="true">

	<description>Shiro Configuration</description>

    <!-- 加载配置属性文件 -->
	<context:property-placeholder ignore-unresolvable="true" location="classpath:/properties/jeeplus.properties" />

	<!-- Shiro权限过滤过滤器定义 -->
	<bean name="shiroFilterChainDefinitions" class="java.lang.String">
		<constructor-arg>
			<value>
				/static/** = anon
				/userfiles/** = anon
				<!--/ccpm_down/** = anon-->
				/ccpm_down_back/** = anon
				/ccpm_query/** = anon
				${adminPath}/xg/attendence/** = anon
				${adminPath}/sys/user/infoCareStatus = anon
				${adminPath}/sys/user/validateLoginName = anon
				${adminPath}/sys/user/validateMobile = anon
				${adminPath}/sys/user/validateMobileExist = anon
				${adminPath}/sys/user/resetPassword = anon
				${adminPath}/sys/register = anon
				${adminPath}/sys/register/registerUser = anon
				${adminPath}/sys/register/getRegisterCode = anon
				${adminPath}/sys/register/validateMobileCode = anon
				${adminPath}/soft/sysVersion/getAndroidVer = anon
				${adminPath}/soft/sysVersion/getIosVer = anon
				${adminPath}/cas = cas
				${adminPath}/login = authc
				${adminPath}/logout = anon
				${adminPath}/** = user
				/act/rest/service/editor/** = perms[act:model:edit]
				/act/rest/service/model/** = perms[act:model:edit]
				/act/rest/service/** = 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="loginUrl" value="${cas.server.url}/login" />-->
		<property name="successUrl" value="${cas.project.url}${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>

	<!-- CAS认证过滤器 -->
	<bean id="casFilter" class="org.apache.shiro.cas.CasFilter">
		<property name="failureUrl" value="${adminPath}/login"/>
	</bean>
	<!--CAS自定义realm-->
	<bean id="casRealm" class="com.jeeplus.common.config.ShiroCasRealm">
		<!--	认证通过后的默认角色	-->
		<property name="defaultRoles" value="ROLE_USER"/>
		<!--	CAS服务端地址前缀	-->
		<property name="casServerUrlPrefix" value="${cas.server.url}"/>
		<property name="casService" value="${cas.project.url}${adminPath}/cas"/>
	</bean>

	<!-- 定义Shiro安全管理配置 -->
	<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
		<property name="realm" ref="casRealm" />
		<property name="sessionManager" ref="sessionManager" />
		<property name="cacheManager" ref="shiroCacheManager" />
	</bean>

	<!-- 自定义会话管理配置 -->
	<bean id="sessionManager" class="com.jeeplus.core.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="jeeplus.session.id"/>
	</bean>

	<!-- 自定义Session存储容器 -->

	<bean id="sessionDAO" class="com.jeeplus.core.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.jeeplus.core.security.shiro.cache.SessionCacheManager" /> -->
	<bean id="shiroCacheManager" class="org.nutz.j2cache.shiro.J2CacheManager">
	</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>

四:创建shiroCasRealm类

package com.jeeplus.common.config;

import com.jeeplus.common.utils.SpringContextHolder;
import com.jeeplus.core.web.Servlets;
import com.jeeplus.modules.sys.entity.Menu;
import com.jeeplus.modules.sys.entity.Role;
import com.jeeplus.modules.sys.entity.User;
import com.jeeplus.modules.sys.mapper.UserMapper;
import com.jeeplus.modules.sys.security.SystemAuthorizingRealm;
import com.jeeplus.modules.sys.service.SystemService;
import com.jeeplus.modules.sys.utils.LogUtils;
import com.jeeplus.modules.sys.utils.UserUtils;
import org.apache.shiro.SecurityUtils;
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.apache.shiro.subject.Subject;
import org.apache.shiro.util.CollectionUtils;
import org.apache.shiro.util.StringUtils;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

import java.util.Collection;
import java.util.List;

/**
 * @author: 王强
 * @create: 2023-04-17 09:27
 **/
@Configuration
public class ShiroCasRealm extends CasRealm {
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private SystemService systemService;

    /**
     * 获取系统业务对象
     */
    public SystemService getSystemService() {
        if (systemService == null){
            systemService = SpringContextHolder.getBean(SystemService.class);
        }
        return systemService;
    }


    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

        Subject subject = SecurityUtils.getSubject();
        principals = subject.getPrincipals();
        User user2 = (User) principals.getPrimaryPrincipal();
        SystemAuthorizingRealm.Principal principal = new SystemAuthorizingRealm.Principal(user2,false);

//        SystemAuthorizingRealm.Principal principal = (SystemAuthorizingRealm.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 (org.apache.commons.lang3.StringUtils.isNotBlank(menu.getPermission())){
                    // 添加基于Permission的权限信息
                    for (String permission : org.apache.commons.lang3.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;
        }
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        CasToken casToken = (CasToken) token;

        // token为空直接返回,页面会重定向到 Cas Server 登录页,并且携带本项目回调页
        if (token == null) {
            return null;
        }

        // 获取服务端范围的票根
        String ticket = (String) casToken.getCredentials();

        // 票根为空直接返回,页面会重定向到 Cas Server 登录页,并且携带本项目回调页
        if (!StringUtils.hasText(ticket)) {
            return null;
        }

        TicketValidator ticketValidator = ensureTicketValidator();

        try {
            // 票根验证
            Assertion casAssertion = ticketValidator.validate(ticket, getCasService());
            // 获取服务端返回的用户数据
            AttributePrincipal casPrincipal = casAssertion.getPrincipal();

            // 拿到用户唯一标识
            String userId = casPrincipal.getName();

            // 通过唯一标识查询数据库用户表
            // 如果查询到对应用户则直接返回用户数据
            // 如果没有查询到用户数据则向数据库新增用户并返回用户数据
            User user = new User();
            user.setLoginName(userId);
            User member = userMapper.getByLoginName(user);

            // 将获取到的本项目数据库用户包装为 shiro 自身的 principal 存于当前 session 中
            // 之后在整个项目中都可以通过 SecurityUtils.getSubject().getPrincipal() 直接获取到当前用户信息
            List<Object> principals = CollectionUtils.asList(member, casPrincipal.getAttributes());
            PrincipalCollection principalCollection = new SimplePrincipalCollection(principals, getName());

            return new SimpleAuthenticationInfo(principalCollection, ticket);
        } catch (TicketValidationException e) {
            throw new CasAuthenticationException("Unable to validate ticket [" + ticket + "]", e);
        }
    }
}

参考

https://blog.csdn.net/yy251066394/article/details/78748681?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-78748681-blog-53349394.235%5Ev29%5Epc_relevant_default_base3&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1-78748681-blog-53349394.235%5Ev29%5Epc_relevant_default_base3&utm_relevant_index=2

https://www.jianshu.com/p/3c6680daab0e

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值