暂定逻辑如下:

搭建CAS服务器端:

项目地址:https://gitee.com/weigang_wu/cas-server-webapp.git

项目里有二开的说明文档,如:

按照自定义的数据库校验修改如下:

首先:修改数据库连接以及查询数据

这里我的数据库是8.0的所以需要升级驱动包:

然后增加md5的工具类:

代码就不赘述了,网上一大把

然后在登录校验的地方增加如下代码:

密码加密方式(参照ruoyi密码加密方式)用户名+密码+salt

当然你也可以修改一下样式等等

WEB-INF\view\jsp\default\ui登陆界面:casLoginView.jsp登陆成功:casGenericSuccess.jsp登出界面:casLogoutView.jsp

可以直接用maven打包然后扔进tomcat运行,也可以用IDEA运行:

IDEA部署Tomcat(超详细)_idea配置tomcat_秃头披风侠.的博客-CSDN博客

ruoyi修改:

1.修改数据库参数

2.framework增加依赖

<!-- pac4j安全引擎 --><dependency><groupId>org.pac4j</groupId><artifactId>pac4j-cas</artifactId><version>3.0.2</version></dependency><dependency><groupId>io.buji</groupId><artifactId>buji-pac4j</artifactId><version>4.0.0</version><exclusions><exclusion><artifactId>shiro-web</artifactId><groupId>org.apache.shiro</groupId></exclusion></exclusions></dependency>

3.yml增加配置

# cas配置
cas:client-name: CasClientserver:url: http://192.168.1.122:8080/cas_server_webapp_warproject:url: http://192.168.1.122:80

4.增加com.ruoyi.framework.config.Pac4jConfig

package com.ruoyi.framework.config;import org.pac4j.cas.config.CasConfiguration;
import org.pac4j.cas.config.CasProtocol;
import org.pac4j.core.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import com.ruoyi.framework.shiro.client.Pac4jCasClient;import io.buji.pac4j.context.ShiroSessionStore;@Configuration
public class Pac4jConfig {/** 地址为:cas地址 */@Value("${cas.server.url}")private String casServerUrl;/** 地址为:验证返回后的项目地址:http://localhost:8081 */@Value("${cas.project.url}")private String projectUrl;/** 相当于一个标志,可以随意 */@Value("${cas.client-name}")private String clientName;/***  pac4j配置* @param casClient* @param shiroSessionStore* @return*/@Bean("authcConfig")public Config config(Pac4jCasClient casClient, ShiroSessionStore shiroSessionStore) {Config config = new Config(casClient);config.setSessionStore(shiroSessionStore);return config;}/*** 自定义存储* @return*/@Beanpublic ShiroSessionStore shiroSessionStore(){return new ShiroSessionStore();}/*** cas 客户端配置* @param casConfig* @return*/@Beanpublic Pac4jCasClient casClient(CasConfiguration casConfig){Pac4jCasClient casClient = new Pac4jCasClient(casConfig);//客户端回调地址casClient.setCallbackUrl(projectUrl + "/callback?client_name=" + clientName);casClient.setName(clientName);return casClient;}/*** 请求cas服务端配置*/@Beanpublic CasConfiguration casConfig(){final CasConfiguration configuration = new CasConfiguration();//CAS server登录地址configuration.setLoginUrl(casServerUrl + "/login");//CAS 版本,默认为 CAS30,我们使用的是 CAS20configuration.setProtocol(CasProtocol.CAS20);configuration.setAcceptAnyProxy(true);configuration.setPrefixUrl(casServerUrl + "/");return configuration;}}

5.com.ruoyi.framework.config.ShiroConfig修改

package com.ruoyi.framework.config;import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import javax.servlet.Filter;import com.ruoyi.framework.shiro.realm.CasRealm;
import io.buji.pac4j.filter.CallbackFilter;
import io.buji.pac4j.filter.SecurityFilter;
import io.buji.pac4j.subject.Pac4jSubjectFactory;
import org.apache.commons.io.IOUtils;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.config.ConfigurationException;
import org.apache.shiro.io.ResourceUtils;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.pac4j.core.config.Config;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.spring.SpringUtils;
import com.ruoyi.framework.shiro.realm.UserRealm;
import com.ruoyi.framework.shiro.session.OnlineSessionDAO;
import com.ruoyi.framework.shiro.session.OnlineSessionFactory;
import io.buji.pac4j.filter.LogoutFilter;
//import com.ruoyi.framework.shiro.web.filter.LogoutFilter;
import com.ruoyi.framework.shiro.web.filter.captcha.CaptchaValidateFilter;
import com.ruoyi.framework.shiro.web.filter.kickout.KickoutSessionFilter;
import com.ruoyi.framework.shiro.web.filter.online.OnlineSessionFilter;
import com.ruoyi.framework.shiro.web.filter.sync.SyncOnlineSessionFilter;
import com.ruoyi.framework.shiro.web.session.OnlineWebSessionManager;
import com.ruoyi.framework.shiro.web.session.SpringSessionValidationScheduler;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;/*** 权限配置加载** @author ruoyi*/
@Configuration
public class ShiroConfig
{public static final String PREMISSION_STRING = "perms[\"{0}\"]";/*** Session超时时间,单位为毫秒(默认30分钟)*/@Value("${shiro.session.expireTime}")private int expireTime;/*** 相隔多久检查一次session的有效性,单位毫秒,默认就是10分钟*/@Value("${shiro.session.validationInterval}")private int validationInterval;/*** 同一个用户最大会话数*/@Value("${shiro.session.maxSession}")private int maxSession;/*** 踢出之前登录的/之后登录的用户,默认踢出之前登录的用户*/@Value("${shiro.session.kickoutAfter}")private boolean kickoutAfter;/*** 验证码开关*/@Value("${shiro.user.captchaEnabled}")private boolean captchaEnabled;/*** 验证码类型*/@Value("${shiro.user.captchaType}")private String captchaType;/*** 设置Cookie的域名*/@Value("${shiro.cookie.domain}")private String domain;/*** 设置cookie的有效访问路径*/@Value("${shiro.cookie.path}")private String path;/*** 设置HttpOnly属性*/@Value("${shiro.cookie.httpOnly}")private boolean httpOnly;/*** 设置Cookie的过期时间,秒为单位*/@Value("${shiro.cookie.maxAge}")private int maxAge;/*** 登录地址*/@Value("${shiro.user.loginUrl}")private String loginUrl;/*** 权限认证失败地址*/@Value("${shiro.user.unauthorizedUrl}")private String unauthorizedUrl;/* liupsh cas begin*//** 项目工程路径 */@Value("${cas.project.url}")private String projectUrl;/** 项目cas服务路径 */@Value("${cas.server.url}")private String casServerUrl;/** 客户端名称 */@Value("${cas.client-name}")private String clientName;/* liupsh cas end*//*** 缓存管理器 使用Ehcache实现*/@Beanpublic EhCacheManager getEhCacheManager(){net.sf.ehcache.CacheManager cacheManager = net.sf.ehcache.CacheManager.getCacheManager("ruoyi");EhCacheManager em = new EhCacheManager();if (StringUtils.isNull(cacheManager)){em.setCacheManager(new net.sf.ehcache.CacheManager(getCacheManagerConfigFileInputStream()));return em;}else{em.setCacheManager(cacheManager);return em;}}/*** 返回配置文件流 避免ehcache配置文件一直被占用,无法完全销毁项目重新部署*/protected InputStream getCacheManagerConfigFileInputStream(){String configFile = "classpath:ehcache/ehcache-shiro.xml";InputStream inputStream = null;try{inputStream = ResourceUtils.getInputStreamForPath(configFile);byte[] b = IOUtils.toByteArray(inputStream);InputStream in = new ByteArrayInputStream(b);return in;}catch (IOException e){throw new ConfigurationException("Unable to obtain input stream for cacheManagerConfigFile [" + configFile + "]", e);}finally{IOUtils.closeQuietly(inputStream);}}/*** 自定义Realm*/@Beanpublic UserRealm userRealm(EhCacheManager cacheManager){UserRealm userRealm = new UserRealm();userRealm.setCacheManager(cacheManager);return userRealm;}/*** 自定义sessionDAO会话*/@Beanpublic OnlineSessionDAO sessionDAO(){OnlineSessionDAO sessionDAO = new OnlineSessionDAO();return sessionDAO;}/*** 自定义sessionFactory会话*/@Beanpublic OnlineSessionFactory sessionFactory(){OnlineSessionFactory sessionFactory = new OnlineSessionFactory();return sessionFactory;}/*** 会话管理器*/@Beanpublic OnlineWebSessionManager sessionManager(){OnlineWebSessionManager manager = new OnlineWebSessionManager();// 加入缓存管理器manager.setCacheManager(getEhCacheManager());// 删除过期的sessionmanager.setDeleteInvalidSessions(true);// 设置全局session超时时间manager.setGlobalSessionTimeout(expireTime * 60 * 1000);// 去掉 JSESSIONIDmanager.setSessionIdUrlRewritingEnabled(false);// 定义要使用的无效的Session定时调度器manager.setSessionValidationScheduler(SpringUtils.getBean(SpringSessionValidationScheduler.class));// 是否定时检查sessionmanager.setSessionValidationSchedulerEnabled(true);// 自定义SessionDaomanager.setSessionDAO(sessionDAO());// 自定义sessionFactorymanager.setSessionFactory(sessionFactory());return manager;}/*** 安全管理器*/@Beanpublic SecurityManager securityManager(CasRealm casRealm){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();// 设置realm.securityManager.setRealm(casRealm);// 记住我securityManager.setRememberMeManager(rememberMeManager());// 注入缓存管理器;securityManager.setCacheManager(getEhCacheManager());// session管理器securityManager.setSessionManager(sessionManager());securityManager.setSubjectFactory(subjectFactory());return securityManager;}/*** 使用 pac4j 的 subjectFactory* @return*/@Beanpublic Pac4jSubjectFactory subjectFactory(){return new Pac4jSubjectFactory();}@Beanpublic CasRealm casRealm(){CasRealm realm = new CasRealm();// 使用自定义的realmrealm.setClientName(clientName);realm.setCachingEnabled(false);//暂时不使用缓存realm.setAuthenticationCachingEnabled(false);realm.setAuthorizationCachingEnabled(false);//realm.setAuthenticationCacheName("authenticationCache");//realm.setAuthorizationCacheName("authorizationCache");return realm;}/*** 加载shiroFilter权限控制规则(从数据库读取然后配置)* @param shiroFilterFactoryBean*/private void loadShiroFilterChain(ShiroFilterFactoryBean shiroFilterFactoryBean){/*下面这些规则配置最好配置到配置文件中 */Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();// 对静态资源设置匿名访问filterChainDefinitionMap.put("/favicon.ico**", "anon");filterChainDefinitionMap.put("/ruoyi.png**", "anon");filterChainDefinitionMap.put("/css/**", "anon");filterChainDefinitionMap.put("/docs/**", "anon");filterChainDefinitionMap.put("/fonts/**", "anon");filterChainDefinitionMap.put("/img/**", "anon");filterChainDefinitionMap.put("/ajax/**", "anon");filterChainDefinitionMap.put("/js/**", "anon");filterChainDefinitionMap.put("/ruoyi/**", "anon");filterChainDefinitionMap.put("/druid/**", "anon");filterChainDefinitionMap.put("/captcha/captchaImage**", "anon");//内部接口调用filterChainDefinitionMap.put("/hiapi/**", "anon");filterChainDefinitionMap.put("/", "securityFilter");filterChainDefinitionMap.put("/application/**", "securityFilter");filterChainDefinitionMap.put("/index", "securityFilter");filterChainDefinitionMap.put("/callback", "callbackFilter");filterChainDefinitionMap.put("/logout", "logout");
//        filterChainDefinitionMap.put("/**","anon");shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);}/*** Shiro过滤器配置*/@Beanpublic ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager , Config config){ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();// Shiro的核心安全接口,这个属性是必须的shiroFilterFactoryBean.setSecurityManager(securityManager);// 添加casFilter到shiroFilter中loadShiroFilterChain(shiroFilterFactoryBean);Map<String, Filter> filters = new HashMap<>(3);//cas 资源认证拦截器SecurityFilter securityFilter = new SecurityFilter();securityFilter.setConfig(config);securityFilter.setClients(clientName);filters.put("securityFilter", securityFilter);//cas 认证后回调拦截器CallbackFilter callbackFilter = new CallbackFilter();callbackFilter.setConfig(config);callbackFilter.setDefaultUrl(projectUrl);filters.put("callbackFilter", callbackFilter);// 注销 拦截器LogoutFilter logoutFilter = new LogoutFilter();logoutFilter.setConfig(config);logoutFilter.setCentralLogout(true);logoutFilter.setLocalLogout(true);logoutFilter.setDefaultUrl(projectUrl + "/callback?client_name=" + clientName);filters.put("logout",logoutFilter);shiroFilterFactoryBean.setFilters(filters);return shiroFilterFactoryBean;}/*** 自定义在线用户处理过滤器*/@Beanpublic OnlineSessionFilter onlineSessionFilter(){OnlineSessionFilter onlineSessionFilter = new OnlineSessionFilter();onlineSessionFilter.setLoginUrl(loginUrl);return onlineSessionFilter;}/*** 自定义在线用户同步过滤器*/@Beanpublic SyncOnlineSessionFilter syncOnlineSessionFilter(){SyncOnlineSessionFilter syncOnlineSessionFilter = new SyncOnlineSessionFilter();return syncOnlineSessionFilter;}/*** 自定义验证码过滤器*/@Beanpublic CaptchaValidateFilter captchaValidateFilter(){CaptchaValidateFilter captchaValidateFilter = new CaptchaValidateFilter();captchaValidateFilter.setCaptchaEnabled(captchaEnabled);captchaValidateFilter.setCaptchaType(captchaType);return captchaValidateFilter;}/*** cookie 属性设置*/public SimpleCookie rememberMeCookie(){SimpleCookie cookie = new SimpleCookie("rememberMe");cookie.setDomain(domain);cookie.setPath(path);cookie.setHttpOnly(httpOnly);cookie.setMaxAge(maxAge * 24 * 60 * 60);return cookie;}/*** 记住我*/public CookieRememberMeManager rememberMeManager(){CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();cookieRememberMeManager.setCookie(rememberMeCookie());cookieRememberMeManager.setCipherKey(Base64.decode("fCq+/xW488hMTCD+cmJ3aQ=="));return cookieRememberMeManager;}/*** 同一个用户多设备登录限制*/public KickoutSessionFilter kickoutSessionFilter(){KickoutSessionFilter kickoutSessionFilter = new KickoutSessionFilter();kickoutSessionFilter.setCacheManager(getEhCacheManager());kickoutSessionFilter.setSessionManager(sessionManager());// 同一个用户最大的会话数,默认-1无限制;比如2的意思是同一个用户允许最多同时两个人登录kickoutSessionFilter.setMaxSession(maxSession);// 是否踢出后来登录的,默认是false;即后者登录的用户踢出前者登录的用户;踢出顺序kickoutSessionFilter.setKickoutAfter(kickoutAfter);// 被踢出后重定向到的地址;kickoutSessionFilter.setKickoutUrl("/login?kickout=1");return kickoutSessionFilter;}/*** thymeleaf模板引擎和shiro框架的整合*/@Beanpublic ShiroDialect shiroDialect(){return new ShiroDialect();}/*** 开启Shiro注解通知器*/@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager){AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);return authorizationAttributeSourceAdvisor;}
}

6.新建com.ruoyi.framework.shiro.realm.CasRealm

package com.ruoyi.framework.shiro.realm;import java.util.HashSet;
import java.util.List;
import java.util.Set;import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.pac4j.core.profile.CommonProfile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;import com.alibaba.fastjson.JSON;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.exception.user.CaptchaException;
import com.ruoyi.common.exception.user.RoleBlockedException;
import com.ruoyi.common.exception.user.UserBlockedException;
import com.ruoyi.common.exception.user.UserNotExistsException;
import com.ruoyi.common.exception.user.UserPasswordNotMatchException;
import com.ruoyi.common.exception.user.UserPasswordRetryLimitExceedException;
import com.ruoyi.common.utils.ShiroUtils;
import com.ruoyi.framework.shiro.service.SysLoginService;
import com.ruoyi.system.service.ISysMenuService;
import com.ruoyi.system.service.ISysRoleService;import io.buji.pac4j.realm.Pac4jRealm;
import io.buji.pac4j.subject.Pac4jPrincipal;
import io.buji.pac4j.token.Pac4jToken;/*** 认证与授权**/
public class CasRealm extends Pac4jRealm {private final static Logger log = LoggerFactory.getLogger(CasRealm.class);private String clientName;@Autowiredprivate ISysMenuService menuService;@Autowiredprivate ISysRoleService roleService;@Autowiredprivate SysLoginService loginService;public String getClientName() {return clientName;}public void setClientName(String clientName) {this.clientName = clientName;}public ISysMenuService getMenuService() {return menuService;}public void setMenuService(ISysMenuService menuService) {this.menuService = menuService;}public ISysRoleService getRoleService() {return roleService;}public void setRoleService(ISysRoleService roleService) {this.roleService = roleService;}public SysLoginService getLoginService() {return loginService;}public void setLoginService(SysLoginService loginService) {this.loginService = loginService;}/*** 认证* @param authenticationToken* @return* @throws AuthenticationException*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {final Pac4jToken pac4jToken = (Pac4jToken) authenticationToken;System.out.println(JSON.toJSON(pac4jToken).toString());final List<CommonProfile> commonProfileList = pac4jToken.getProfiles();final CommonProfile commonProfile = commonProfileList.get(0);//todofinal Pac4jPrincipal principal = new Pac4jPrincipal(commonProfileList, getPrincipalNameAttribute());final PrincipalCollection principalCollection = new SimplePrincipalCollection(principal, getName());String username = commonProfile.getId();SysUser user = null;try{user = loginService.casLogin(username);}catch (CaptchaException e){throw new AuthenticationException(e.getMessage(), e);}catch (UserNotExistsException e){throw new UnknownAccountException(e.getMessage(), e);}catch (UserPasswordNotMatchException e){throw new IncorrectCredentialsException(e.getMessage(), e);}catch (UserPasswordRetryLimitExceedException e){throw new ExcessiveAttemptsException(e.getMessage(), e);}catch (UserBlockedException e){throw new LockedAccountException(e.getMessage(), e);}catch (RoleBlockedException e){throw new LockedAccountException(e.getMessage(), e);}catch (Exception e){log.info("对用户[" + username + "]进行登录验证..验证未通过{}", e.getMessage());throw new AuthenticationException(e.getMessage(), e);}return new SimpleAuthenticationInfo(user, commonProfileList.hashCode(), getName());}/*** 授权/验权(todo 后续有权限在此增加)* @param principals* @return*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();SysUser user = ShiroUtils.getSysUser();// 角色列表Set<String> roles = new HashSet<String>();// 功能列表Set<String> menus = new HashSet<String>();// 管理员拥有所有权限if (user.isAdmin()){info.addRole("admin");info.addStringPermission("*:*:*");}else{roles = roleService.selectRoleKeys(user.getUserId());menus = menuService.selectPermsByUserId(user.getUserId());// 角色加入AuthorizationInfo认证对象info.setRoles(roles);// 权限加入AuthorizationInfo认证对象info.setStringPermissions(menus);}return info;}public static void main(String[] args) {encryptPassword();}public static void encryptPassword(){System.out.println(new Md5Hash("admin"+"admin123"+"111111").toHex());}}

7.修改com.ruoyi.framework.shiro.service.SysLoginService

package com.ruoyi.framework.shiro.service;import java.util.List;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.constant.ShiroConstants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.enums.UserStatus;
import com.ruoyi.common.exception.user.BlackListException;
import com.ruoyi.common.exception.user.CaptchaException;
import com.ruoyi.common.exception.user.UserBlockedException;
import com.ruoyi.common.exception.user.UserDeleteException;
import com.ruoyi.common.exception.user.UserNotExistsException;
import com.ruoyi.common.exception.user.UserPasswordNotMatchException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.IpUtils;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.ShiroUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.framework.manager.factory.AsyncFactory;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.system.service.ISysMenuService;
import com.ruoyi.system.service.ISysUserService;/*** 登录校验方法* * @author ruoyi*/
@Component
public class SysLoginService
{@Autowiredprivate SysPasswordService passwordService;@Autowiredprivate ISysUserService userService;@Autowiredprivate ISysMenuService menuService;@Autowiredprivate ISysConfigService configService;/*** 登录*/public SysUser login(String username, String password){// 验证码校验if (ShiroConstants.CAPTCHA_ERROR.equals(ServletUtils.getRequest().getAttribute(ShiroConstants.CURRENT_CAPTCHA))){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));throw new CaptchaException();}// 用户名或密码为空 错误if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("not.null")));throw new UserNotExistsException();}// 密码如果不在指定范围内 错误if (password.length() < UserConstants.PASSWORD_MIN_LENGTH|| password.length() > UserConstants.PASSWORD_MAX_LENGTH){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));throw new UserPasswordNotMatchException();}// 用户名不在指定范围内 错误if (username.length() < UserConstants.USERNAME_MIN_LENGTH|| username.length() > UserConstants.USERNAME_MAX_LENGTH){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));throw new UserPasswordNotMatchException();}// IP黑名单校验String blackStr = configService.selectConfigByKey("sys.login.blackIPList");if (IpUtils.isMatchedIp(blackStr, ShiroUtils.getIp())){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("login.blocked")));throw new BlackListException();}// 查询用户信息SysUser user = userService.selectUserByLoginName(username);/**if (user == null && maybeMobilePhoneNumber(username)){user = userService.selectUserByPhoneNumber(username);}if (user == null && maybeEmail(username)){user = userService.selectUserByEmail(username);}*/if (user == null){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.not.exists")));throw new UserNotExistsException();}if (UserStatus.DELETED.getCode().equals(user.getDelFlag())){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.delete")));throw new UserDeleteException();}if (UserStatus.DISABLE.getCode().equals(user.getStatus())){AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.blocked", user.getRemark())));throw new UserBlockedException();}passwordService.validate(user, password);AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));setRolePermission(user);recordLoginInfo(user.getUserId());return user;}/*** cas登录*/public SysUser casLogin(String username){// 查询用户信息SysUser user = userService.selectUserByLoginName(username);//        AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));setRolePermission(user);recordLoginInfo(user.getUserId());return user;}/**private boolean maybeEmail(String username){if (!username.matches(UserConstants.EMAIL_PATTERN)){return false;}return true;}private boolean maybeMobilePhoneNumber(String username){if (!username.matches(UserConstants.MOBILE_PHONE_NUMBER_PATTERN)){return false;}return true;}*//*** 设置角色权限** @param user 用户信息*/public void setRolePermission(SysUser user){List<SysRole> roles = user.getRoles();if (!roles.isEmpty() && roles.size() > 1){// 多角色设置permissions属性,以便数据权限匹配权限for (SysRole role : roles){Set<String> rolePerms = menuService.selectPermsByRoleId(role.getRoleId());role.setPermissions(rolePerms);}}}/*** 记录登录信息** @param userId 用户ID*/public void recordLoginInfo(Long userId){SysUser user = new SysUser();user.setUserId(userId);user.setLoginIp(ShiroUtils.getIp());user.setLoginDate(DateUtils.getNowDate());userService.updateUserInfo(user);}
}

8.Pac4jCasClient

package com.ruoyi.framework.shiro.client;import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.pac4j.cas.config.CasConfiguration;
import org.pac4j.core.context.Pac4jConstants;
import org.pac4j.core.context.WebContext;
import org.pac4j.core.context.session.SessionStore;
import org.pac4j.core.redirect.RedirectAction;
import org.pac4j.core.util.CommonHelper;
import org.pac4j.cas.client.CasClient;public class Pac4jCasClient extends CasClient {public Pac4jCasClient() {super();}public Pac4jCasClient(CasConfiguration configuration) {super(configuration);}/** (non-Javadoc)* @see org.pac4j.core.client.IndirectClient#getRedirectAction(org.pac4j.core.context.WebContext)*/@Overridepublic RedirectAction getRedirectAction(WebContext context) {this.init();if (getAjaxRequestResolver().isAjax(context)) {this.logger.info("AJAX request detected -> returning the appropriate action");RedirectAction action = getRedirectActionBuilder().redirect(context);this.cleanRequestedUrl(context);return getAjaxRequestResolver().buildAjaxResponse(action.getLocation(), context);} else {final String attemptedAuth = (String)context.getSessionStore().get(context, this.getName() + ATTEMPTED_AUTHENTICATION_SUFFIX);if (CommonHelper.isNotBlank(attemptedAuth)) {this.cleanAttemptedAuthentication(context);this.cleanRequestedUrl(context);//这里按自己需求处理,默认是返回了401,我在这边改为跳转到cas登录页面return this.getRedirectActionBuilder().redirect(context);} else {return this.getRedirectActionBuilder().redirect(context);}}}private void cleanRequestedUrl(WebContext context) {SessionStore<WebContext> sessionStore = context.getSessionStore();if (sessionStore.get(context, Pac4jConstants.REQUESTED_URL) != null) {sessionStore.set(context, Pac4jConstants.REQUESTED_URL, "");}}private void cleanAttemptedAuthentication(WebContext context) {SessionStore<WebContext> sessionStore = context.getSessionStore();if (sessionStore.get(context, this.getName() + ATTEMPTED_AUTHENTICATION_SUFFIX) != null) {sessionStore.set(context, this.getName() + ATTEMPTED_AUTHENTICATION_SUFFIX, "");}}}

参考链接:

Ruoyi若依前后端一体项目整合cas单点登录_ruoyi 单点登录_飞翔的佩奇的博客-CSDN博客

插件集成 | RuoYi

然后先启动cas 服务端,然后再启动ruoyi项目

输入ruoyi项目地址会自动跳转cas认证,登录完毕之后又会跳转回ruoyi项目。

统一认证测试:

将ruoyi打包运行

然后依次测试即可

ruoyi对接CAS统一身份认证相关推荐

  1. CAS统一身份认证(四):集成MySQL用户验证

    本文主要介绍CAS统一身份认证服务器JDBC密码管理,并以FreeBSD环境下的MySQL数据库为例实现CAS 6.6版的数据库用户验证.主要包括以下几个方面: JDBC密码管理 MySQL数据库准备 ...

  2. springmvc--sso单点登录cas统一身份认证器

    开发环境 maven idea Windows 10 JDK 1.8+ 域名解析的配置 这里通过SwitchHosts来实现:以管理员身份打开 前两个:两个客户端应用的域名 后一个:是服务端的域名. ...

  3. CAS 统一身份认证(一):系统编译与运行

    本文主要介绍CAS中央身份认证服务,详细说明了FreeBSD环境下CAS 6.5版的下载.编译.部署和运行.主要包括以下几个方面: 中央身份认证服务概述 CAS 6.5的安装 本文使用的软件版本: F ...

  4. .net core集成cas统一身份认证

    git源码地址:https://github.com/IUCrimson/AspNet.Security.CAS 安装 NuGet 包 PM> Install-Package AspNetCor ...

  5. 钉钉统一身份认证对接前后端代码

    1. 钉钉统一身份认证平台的对接方式 钉钉统一身份认证平台提供了多种对接方式,包括OAuth2.0.SAML.CAS等,其中OAuth2.0是最常用的一种方式.OAuth2.0的对接流程如下: 1.开 ...

  6. cas如何实现多系统间的相互认证_统一身份认证和单点登录的区别

    首先大家会遇到这样一个问题,统一身份认证和单点登录的概念是什么? 百度百科对统一身份认证的定义 所谓身份认证,就是判断一个用户是否为合法用户的处理过程.最常用的简单身份认证方式是系统通过核对用户输入的 ...

  7. 统一身份认证简单对接流程

    随着信息化推进,各个政府单位都建设有自己的应用平台(类似portal门户中应用导航),统一身份认证系统(SSO-SERVER),后续其他单位建设的系统都注册到统一应用平台中. 在统一平台登陆后,免密登 ...

  8. 智慧校园下“企业微信+CAS”的统一身份认证方案设计

    摘要 在智慧校园建设不断推进的背景下,云计算.物联网.大数据等新兴技术已深入到学校管理.教学的方方面面.统一身份认证系统在智慧校园总体框架中位于平台支撑层,在智慧校园体系中提供身份认证.应用授权.角色 ...

  9. 【集合】统一身份认证(CAS)和OAuth2的工作流程

    一.CAS协议 单点登录SSO(Single Sign ON),指在多个应用系统中,只需登录一次,即可在多个应用系统之间共享登录.如:在学校登录了OA系统,再打开科研.教务系统,都会实现自动登录. 统 ...

最新文章

  1. TVM 图优化Graph Optimization
  2. Python中的特殊成员和魔法方法
  3. [Android] Android颜色对应的xml配置值
  4. weblogic从入门到起飞!(域模块、扩展模块)(三)
  5. JSON 转javabean 利器
  6. BZOJ4504. K个串(主席树+优先队列)
  7. cad lisp 二次抛物线_学习CAD的五个段位,你是青铜还是王者?
  8. js上传视频,预览视频
  9. Android 中像素px和dp的转化
  10. 用python实现的仿真程序_如何实现疫情扩散仿真程序?
  11. laravel 任务队列_Laravel 队列系统实现及使用教程
  12. PS如何做文字扫描效果
  13. jar文件打不开,用什么打开
  14. Python12306自动抢票下单,五一旅游回家就选Python
  15. 图片转成base64格式上传至数据库
  16. 5个Libra协会成员加入,这家创业公司凭什么与Facebook 竞争?
  17. Module Error (from ./node_modules/vue-loader/lib/loaders/templateLoader.js):(Emitted value instead o
  18. Features and Characteristics
  19. for单次循环参数对比-以ode45求一元二阶微分方程为例
  20. 763.Partition Labels (Medium)

热门文章

  1. String类的切割功能
  2. 软件销售需要具备的基本素质
  3. 数据库安全性概述及TCSEC/TDI安全性能指标
  4. Ubuntu20.04 卸载cuda 11.0
  5. 如何在新版本的万能地图下载器内切换地图
  6. 章鱼网络进展月报 | 2021.11.1-11.30
  7. 初学者之路——————离散卷积
  8. muduo源码分析之回调模块
  9. wireshark抓包分析FTP
  10. plotly可视化绘制双坐标轴图