From 47e2f219f54e7b3f4c0ffa8bb4760b7bd408aa0a Mon Sep 17 00:00:00 2001 From: thinkgem Date: Wed, 11 Jul 2018 22:40:59 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8B=86=E7=A6=BB=E5=87=BACAS=E6=9D=83?= =?UTF-8?q?=E9=99=90=E6=8E=88=E6=9D=83=E7=9A=84Realm=EF=BC=8C=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=BC=80=E5=8F=91=E8=80=85=E8=87=AA=E5=AE=9A=E4=B9=89?= =?UTF-8?q?Realm=EF=BC=88=E4=B8=AA=E4=BA=BA=E7=89=88=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../shiro/filter/CasAuthenticationFilter.java | 14 +- .../filter/FormAuthenticationFilter.java | 5 +- .../common/shiro/filter/LogoutFilter.java | 21 +- .../common/shiro/realm/AuthorizingRealm.java | 51 +--- .../shiro/realm/CasAuthorizingRealm.java | 242 ++++++++++++++++++ .../jeesite/modules/config/ShiroConfig.java | 86 ++++--- .../modules/sys/web/LoginController.java | 2 +- .../main/resources/config/jeesite-core.yml | 9 +- .../i18n/core/common/i18n_en.properties | 1 - .../i18n/core/common/i18n_zh_CN.properties | 1 - web/src/main/resources/config/jeesite.yml | 27 +- 11 files changed, 355 insertions(+), 104 deletions(-) create mode 100644 modules/core/src/main/java/com/jeesite/common/shiro/realm/CasAuthorizingRealm.java diff --git a/modules/core/src/main/java/com/jeesite/common/shiro/filter/CasAuthenticationFilter.java b/modules/core/src/main/java/com/jeesite/common/shiro/filter/CasAuthenticationFilter.java index b7bce93b..34df47fc 100644 --- a/modules/core/src/main/java/com/jeesite/common/shiro/filter/CasAuthenticationFilter.java +++ b/modules/core/src/main/java/com/jeesite/common/shiro/filter/CasAuthenticationFilter.java @@ -8,6 +8,7 @@ import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationToken; @@ -15,17 +16,18 @@ import org.apache.shiro.subject.Subject; import org.apache.shiro.web.util.WebUtils; import com.jeesite.common.lang.StringUtils; -import com.jeesite.common.shiro.realm.BaseAuthorizingRealm; +import com.jeesite.common.shiro.realm.CasAuthorizingRealm; +import com.jeesite.common.shiro.realm.LoginInfo; /** * CAS过滤器 * @author ThinkGem - * @version 2017-03-22 + * @version 2018-7-11 */ @SuppressWarnings("deprecation") public class CasAuthenticationFilter extends org.apache.shiro.cas.CasFilter { - private BaseAuthorizingRealm authorizingRealm; // 安全认证类 + private CasAuthorizingRealm authorizingRealm; // 安全认证类 /** * 登录成功调用事件 @@ -34,8 +36,8 @@ public class CasAuthenticationFilter extends org.apache.shiro.cas.CasFilter { protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception { // 登录成功后初始化授权信息并处理登录后的操作 - authorizingRealm.onLoginSuccess(subject.getPrincipals()); - + authorizingRealm.onLoginSuccess((LoginInfo)subject.getPrincipal(), (HttpServletRequest)request); + String url = request.getParameter("__url"); if (StringUtils.isNotBlank(url)) { WebUtils.issueRedirect(request, response, url, null, true); @@ -80,7 +82,7 @@ public class CasAuthenticationFilter extends org.apache.shiro.cas.CasFilter { } } - public void setAuthorizingRealm(BaseAuthorizingRealm authorizingRealm) { + public void setAuthorizingRealm(CasAuthorizingRealm authorizingRealm) { this.authorizingRealm = authorizingRealm; } diff --git a/modules/core/src/main/java/com/jeesite/common/shiro/filter/FormAuthenticationFilter.java b/modules/core/src/main/java/com/jeesite/common/shiro/filter/FormAuthenticationFilter.java index d4b52e36..5da00345 100644 --- a/modules/core/src/main/java/com/jeesite/common/shiro/filter/FormAuthenticationFilter.java +++ b/modules/core/src/main/java/com/jeesite/common/shiro/filter/FormAuthenticationFilter.java @@ -29,12 +29,13 @@ import com.jeesite.common.lang.StringUtils; import com.jeesite.common.network.IpUtils; import com.jeesite.common.shiro.authc.FormToken; import com.jeesite.common.shiro.realm.BaseAuthorizingRealm; +import com.jeesite.common.shiro.realm.LoginInfo; import com.jeesite.common.web.http.ServletUtils; /** * 表单验证(包含验证码)过滤类 * @author ThinkGem - * @version 2017-03-22 + * @version 2018-7-11 */ public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.FormAuthenticationFilter { @@ -215,7 +216,7 @@ public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc. protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception { // 登录成功后初始化授权信息并处理登录后的操作 - authorizingRealm.onLoginSuccess(subject.getPrincipals()); + authorizingRealm.onLoginSuccess((LoginInfo)subject.getPrincipal(), (HttpServletRequest) request); // 登录操作如果是Ajax操作,直接返回登录信息字符串。 if (ServletUtils.isAjaxRequest((HttpServletRequest) request)) { diff --git a/modules/core/src/main/java/com/jeesite/common/shiro/filter/LogoutFilter.java b/modules/core/src/main/java/com/jeesite/common/shiro/filter/LogoutFilter.java index 9abfc69b..11665427 100644 --- a/modules/core/src/main/java/com/jeesite/common/shiro/filter/LogoutFilter.java +++ b/modules/core/src/main/java/com/jeesite/common/shiro/filter/LogoutFilter.java @@ -15,6 +15,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.jeesite.common.config.Global; +import com.jeesite.common.shiro.realm.BaseAuthorizingRealm; +import com.jeesite.common.shiro.realm.LoginInfo; import com.jeesite.common.web.http.ServletUtils; import com.jeesite.modules.sys.entity.Log; import com.jeesite.modules.sys.utils.LogUtils; @@ -28,6 +30,7 @@ import com.jeesite.modules.sys.utils.UserUtils; public class LogoutFilter extends org.apache.shiro.web.filter.authc.LogoutFilter { private static final Logger log = LoggerFactory.getLogger(LogoutFilter.class); + private BaseAuthorizingRealm authorizingRealm; // 安全认证类 @Override protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception { @@ -36,9 +39,17 @@ public class LogoutFilter extends org.apache.shiro.web.filter.authc.LogoutFilter String redirectUrl = getRedirectUrl(request, response, subject); //try/catch added for SHIRO-298: try { - // 记录用户退出日志 - LogUtils.saveLog(UserUtils.getUser(), ServletUtils.getRequest(), - "系统退出", Log.TYPE_LOGIN_LOGOUT); + // 记录用户退出日志(@Deprecated v4.0.5支持setAuthorizingRealm,之后版本可删除此if子句) + if (authorizingRealm == null){ + LogUtils.saveLog(UserUtils.getUser(), ServletUtils.getRequest(), + "系统退出", Log.TYPE_LOGIN_LOGOUT); + } + // 退出成功之前初始化授权信息并处理登录后的操作 + else{ + authorizingRealm.onLogoutSuccess((LoginInfo)subject.getPrincipal(), + (HttpServletRequest)request); + } + // 退出登录 subject.logout(); } catch (SessionException ise) { @@ -71,5 +82,9 @@ public class LogoutFilter extends org.apache.shiro.web.filter.authc.LogoutFilter } return super.getRedirectUrl(request, response, subject); } + + public void setAuthorizingRealm(BaseAuthorizingRealm authorizingRealm) { + this.authorizingRealm = authorizingRealm; + } } diff --git a/modules/core/src/main/java/com/jeesite/common/shiro/realm/AuthorizingRealm.java b/modules/core/src/main/java/com/jeesite/common/shiro/realm/AuthorizingRealm.java index d118c3ba..f14a0b98 100644 --- a/modules/core/src/main/java/com/jeesite/common/shiro/realm/AuthorizingRealm.java +++ b/modules/core/src/main/java/com/jeesite/common/shiro/realm/AuthorizingRealm.java @@ -3,20 +3,11 @@ */ package com.jeesite.common.shiro.realm; -import java.util.Map; - import javax.servlet.http.HttpServletRequest; -import org.apache.shiro.subject.PrincipalCollection; - -import com.jeesite.common.codec.EncodeUtils; -import com.jeesite.common.lang.ObjectUtils; import com.jeesite.common.utils.SpringUtils; -import com.jeesite.common.web.http.ServletUtils; -import com.jeesite.modules.sys.entity.EmpUser; import com.jeesite.modules.sys.entity.Log; import com.jeesite.modules.sys.entity.User; -import com.jeesite.modules.sys.service.EmpUserService; import com.jeesite.modules.sys.service.UserService; import com.jeesite.modules.sys.utils.LogUtils; import com.jeesite.modules.sys.utils.UserUtils; @@ -24,48 +15,35 @@ import com.jeesite.modules.sys.utils.UserUtils; /** * 系统安全认证实现类 * @author ThinkGem - * @version 2017-03-22 + * @version 2018-7-11 */ -public class AuthorizingRealm extends com.jeesite.common.shiro.realm.BaseAuthorizingRealm { +public class AuthorizingRealm extends BaseAuthorizingRealm { private UserService userService; - private EmpUserService empUserService; public AuthorizingRealm() { super(); } - - @Override - protected void casCreateEmpUser(User user, Map attributes) { - EmpUser empUser = new EmpUser(); - empUser.setIsNewRecord(true); - empUser.setMobile(user.getMobile()); - empUser.setEmail(user.getEmail()); - empUser.setPhone(user.getPhone()); - empUser.getEmployee().getCompany().setCompanyCode(EncodeUtils - .decodeUrl(ObjectUtils.toString(attributes.get("companyCode")))); - empUser.getEmployee().getOffice().setOfficeCode(EncodeUtils - .decodeUrl(ObjectUtils.toString(attributes.get("officeCode")))); - getEmpUserService().save(empUser); - } @Override - public void onLoginSuccess(PrincipalCollection principals) { - super.onLoginSuccess(principals); - - User user = UserUtils.getUser(); + public void onLoginSuccess(LoginInfo loginInfo, HttpServletRequest request) { + super.onLoginSuccess(loginInfo, request); // 更新登录IP、时间、会话ID等 + User user = UserUtils.get(loginInfo.getId()); getUserService().updateUserLoginInfo(user); // 记录用户登录日志 - LogUtils.saveLog(user, ServletUtils.getRequest(), "系统登录", Log.TYPE_LOGIN_LOGOUT); + LogUtils.saveLog(user, request, "系统登录", Log.TYPE_LOGIN_LOGOUT); } @Override - public void onLogoutSuccess(User logoutUser, HttpServletRequest request) { + public void onLogoutSuccess(LoginInfo loginInfo, HttpServletRequest request) { + super.onLogoutSuccess(loginInfo, request); + // 记录用户退出日志 - LogUtils.saveLog(logoutUser, request, "系统退出", Log.TYPE_LOGIN_LOGOUT); + User user = UserUtils.get(loginInfo.getId()); + LogUtils.saveLog(user, request, "系统退出", Log.TYPE_LOGIN_LOGOUT); } public UserService getUserService() { @@ -74,12 +52,5 @@ public class AuthorizingRealm extends com.jeesite.common.shiro.realm.BaseAuthori } return userService; } - - public EmpUserService getEmpUserService() { - if (empUserService == null){ - empUserService = SpringUtils.getBean(EmpUserService.class); - } - return empUserService; - } } diff --git a/modules/core/src/main/java/com/jeesite/common/shiro/realm/CasAuthorizingRealm.java b/modules/core/src/main/java/com/jeesite/common/shiro/realm/CasAuthorizingRealm.java new file mode 100644 index 00000000..e6b11359 --- /dev/null +++ b/modules/core/src/main/java/com/jeesite/common/shiro/realm/CasAuthorizingRealm.java @@ -0,0 +1,242 @@ +/** + * Copyright (c) 2013-Now http://jeesite.com All rights reserved. + */ +package com.jeesite.common.shiro.realm; + +import java.util.Map; + +import javax.servlet.http.HttpServletRequest; +import javax.validation.ValidationException; + +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.jasig.cas.client.authentication.AttributePrincipal; +import org.jasig.cas.client.validation.Assertion; +import org.jasig.cas.client.validation.Cas20ServiceTicketValidator; +import org.jasig.cas.client.validation.TicketValidationException; +import org.jasig.cas.client.validation.TicketValidator; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; + +import com.beust.jcommander.internal.Maps; +import com.jeesite.common.codec.EncodeUtils; +import com.jeesite.common.lang.ObjectUtils; +import com.jeesite.common.shiro.authc.FormToken; +import com.jeesite.common.shiro.cas.CasCreateUser; +import com.jeesite.common.shiro.cas.CasOutHandler; +import com.jeesite.common.utils.SpringUtils; +import com.jeesite.common.web.http.ServletUtils; +import com.jeesite.modules.sys.entity.EmpUser; +import com.jeesite.modules.sys.entity.Log; +import com.jeesite.modules.sys.entity.User; +import com.jeesite.modules.sys.service.EmpUserService; +import com.jeesite.modules.sys.service.UserService; +import com.jeesite.modules.sys.utils.LogUtils; +import com.jeesite.modules.sys.utils.UserUtils; + +/** + * 系统安全认证实现类 + * @author ThinkGem + * @version 2018-7-11 + */ +@SuppressWarnings("deprecation") +public class CasAuthorizingRealm extends BaseAuthorizingRealm { + + private UserService userService; + private EmpUserService empUserService; + + ///////////// CAS ///////////// + private CasOutHandler casOutHandler; + private String casServerUrl; // CAS 服务器地址 + private String casServerCallbackUrl; // CAS 服务器回调地址 + private TicketValidator ticketValidator;// CAS 令牌验证类 + + public CasAuthorizingRealm() { + super(); + this.setAuthenticationTokenClass(CasToken.class); + } + + /* + * 获取登录令牌 + */ + @Override + protected FormToken getFormToken(AuthenticationToken authcToken) { + + // 单点登录登出句柄(登出时注销session)有CAS中央服务器调用 + HttpServletRequest request = ServletUtils.getRequest(); + if (casOutHandler.isLogoutRequest(request)) { + LoginInfo loginInfo = casOutHandler.destroySession(request); + if (loginInfo != null){ + this.onLogoutSuccess(loginInfo, request); + } + return null; + } + + if (authcToken == null){ + return null; + } + + CasToken casToken = (CasToken) authcToken; + String ticket = (String) casToken.getCredentials(); + if (ticketValidator == null) { + ticketValidator = new Cas20ServiceTicketValidator(casServerUrl); + ((Cas20ServiceTicketValidator)ticketValidator).setEncoding("UTF-8"); + } + + // 进行登录身份验证 + Assertion casAssertion = null; + try { + casAssertion = ticketValidator.validate(ticket, casServerCallbackUrl); + } catch (TicketValidationException e) { + // 令牌失效,在LogoutFilter会自动跳转到登录页 + return null; + } + AttributePrincipal casPrincipal = casAssertion.getPrincipal(); + casToken.setUserId(casPrincipal.getName()); + + // 生成登录信息对象 + FormToken token = new FormToken(); + token.setUsername(casPrincipal.getName()); + Map params = Maps.newHashMap(); + params.putAll(casPrincipal.getAttributes()); + params.put("ticket", ticket); + token.setParams(params); + return token; + } + + /* + * 获取用户信息 + */ + @Override + protected User getUserInfo(FormToken token) { + + User user = super.getUserInfo(token); + if (user == null){ + Map attrs = token.getParams(); + + // 如果允许客户端创建账号,则创建账号 + if (ObjectUtils.toBoolean(attrs.get("isAllowClientCreateUser"))){ + + // 获取CAS传递过来的用户属性信息 + user = new User(EncodeUtils.decodeUrl(ObjectUtils.toString(attrs.get("userCode")))); + user.setLoginCode(EncodeUtils.decodeUrl(ObjectUtils.toString(attrs.get("loginCode")))); + user.setPassword(EncodeUtils.decodeUrl(ObjectUtils.toString(attrs.get("password")))); + user.setUserName(EncodeUtils.decodeUrl(ObjectUtils.toString(attrs.get("userName")))); + user.setEmail(EncodeUtils.decodeUrl(ObjectUtils.toString(attrs.get("email")))); + user.setMobile(EncodeUtils.decodeUrl(ObjectUtils.toString(attrs.get("mobile")))); + user.setPhone(EncodeUtils.decodeUrl(ObjectUtils.toString(attrs.get("phone")))); + user.setUserType(EncodeUtils.decodeUrl(ObjectUtils.toString(attrs.get("userType")))); + user.setRefCode(EncodeUtils.decodeUrl(ObjectUtils.toString(attrs.get("refCode")))); + user.setRefName(EncodeUtils.decodeUrl(ObjectUtils.toString(attrs.get("refName")))); + user.setMgrType(EncodeUtils.decodeUrl(ObjectUtils.toString(attrs.get("mgrType")))); + user.setStatus(EncodeUtils.decodeUrl(ObjectUtils.toString(attrs.get("status")))); + + // 如果是员工类型,则平台自动创建 + if (User.USER_TYPE_EMPLOYEE.equals(user.getUserType())){ + + // 保存员工和用户 + try{ + EmpUser empUser = new EmpUser(); + empUser.setIsNewRecord(true); + empUser.setMobile(user.getMobile()); + empUser.setEmail(user.getEmail()); + empUser.setPhone(user.getPhone()); + empUser.getEmployee().getCompany().setCompanyCode(EncodeUtils + .decodeUrl(ObjectUtils.toString(attrs.get("companyCode")))); + empUser.getEmployee().getOffice().setOfficeCode(EncodeUtils + .decodeUrl(ObjectUtils.toString(attrs.get("officeCode")))); + getEmpUserService().save(empUser); + }catch(ValidationException ve){ + throw new AuthenticationException("msg:" + ve.getMessage()); + } + + // 重新获取用户登录 + user = UserUtils.getByLoginCode(token.getUsername()/*, corpCode*/); + if (user != null) { + return user; + } + + } + + // 其它类型,根据项目需要自行创建 + else{ + try{ + CasCreateUser casCreateUser = SpringUtils.getBean(CasCreateUser.class); + if(casCreateUser != null){ + casCreateUser.createUser(user, attrs); + } + }catch(NoSuchBeanDefinitionException e){ + throw new AuthenticationException("msg:用户 “" + token.getUsername() + + "”, 类型 “" + user.getUserType() + "” 在本系统中不存在, 请联系管理员."); + } + } + }else{ + throw new AuthenticationException("msg:用户 “" + token.getUsername() + "” 在本系统中不存在, 请联系管理员."); + } + } + return user; + } + + /* + * 认证密码匹配调用方法 + */ + @Override + protected void assertCredentialsMatch(AuthenticationToken authcToken, + AuthenticationInfo info) throws AuthenticationException { + // CAS的Ticket已经在doGetAuthenticationInfo()认证过了,这里就不验证身份了 + } + + @Override + public void onLoginSuccess(LoginInfo loginInfo, HttpServletRequest request) { + super.onLoginSuccess(loginInfo, request); + + // 单点登录登出句柄(登录时注入session),在这之前必须获取下授权信息 + String ticket = loginInfo.getParam("ticket"); + casOutHandler.recordSession(request, ticket); + //System.out.print("__sid: "+request.getSession().getId()); + //System.out.println(" == "+UserUtils.getSession().getId()); + + // 更新登录IP、时间、会话ID等 + User user = UserUtils.get(loginInfo.getId()); + getUserService().updateUserLoginInfo(user); + + // 记录用户登录日志 + LogUtils.saveLog(user, ServletUtils.getRequest(), "系统登录", Log.TYPE_LOGIN_LOGOUT); + } + + @Override + public void onLogoutSuccess(LoginInfo loginInfo, HttpServletRequest request) { + super.onLogoutSuccess(loginInfo, request); + + // 记录用户退出日志 + User user = UserUtils.get(loginInfo.getId()); + LogUtils.saveLog(user, request, "系统退出", Log.TYPE_LOGIN_LOGOUT); + } + + public UserService getUserService() { + if (userService == null){ + userService = SpringUtils.getBean(UserService.class); + } + return userService; + } + + public EmpUserService getEmpUserService() { + if (empUserService == null){ + empUserService = SpringUtils.getBean(EmpUserService.class); + } + return empUserService; + } + + public void setCasOutHandler(CasOutHandler casOutHandler) { + this.casOutHandler = casOutHandler; + } + + public void setCasServerUrl(String casServerUrl) { + this.casServerUrl = casServerUrl; + } + + public void setCasServerCallbackUrl(String casServerCallbackUrl) { + this.casServerCallbackUrl = casServerCallbackUrl; + } +} diff --git a/modules/core/src/main/java/com/jeesite/modules/config/ShiroConfig.java b/modules/core/src/main/java/com/jeesite/modules/config/ShiroConfig.java index 38b347d4..d04a2f4c 100644 --- a/modules/core/src/main/java/com/jeesite/modules/config/ShiroConfig.java +++ b/modules/core/src/main/java/com/jeesite/modules/config/ShiroConfig.java @@ -3,12 +3,14 @@ */ package com.jeesite.modules.config; +import java.util.Collection; import java.util.Map; import javax.servlet.Filter; import org.apache.shiro.cache.CacheManager; import org.apache.shiro.cas.CasSubjectFactory; +import org.apache.shiro.realm.Realm; import org.apache.shiro.spring.LifecycleBeanPostProcessor; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; @@ -16,6 +18,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.DependsOn; +import com.jeesite.common.collect.ListUtils; import com.jeesite.common.config.Global; import com.jeesite.common.shiro.cas.CasOutHandler; import com.jeesite.common.shiro.config.FilterChainDefinitionMap; @@ -26,6 +29,7 @@ import com.jeesite.common.shiro.filter.PermissionsAuthorizationFilter; import com.jeesite.common.shiro.filter.RolesAuthorizationFilter; import com.jeesite.common.shiro.filter.UserFilter; import com.jeesite.common.shiro.realm.AuthorizingRealm; +import com.jeesite.common.shiro.realm.CasAuthorizingRealm; import com.jeesite.common.shiro.session.SessionDAO; import com.jeesite.common.shiro.session.SessionManager; import com.jeesite.common.shiro.web.ShiroFilterFactoryBean; @@ -34,40 +38,18 @@ import com.jeesite.common.shiro.web.WebSecurityManager; /** * Shiro配置 * @author ThinkGem - * @version 2017年11月30日 + * @version 2018-7-11 */ @SuppressWarnings("deprecation") @Configuration public class ShiroConfig { - /** - * 单点登录信息句柄,单点退出用 - */ - @Bean - public CasOutHandler casOutHandler() { - return new CasOutHandler(); - } - - /** - * 系统安全认证实现类 - */ - @Bean - public AuthorizingRealm authorizingRealm(SessionDAO sessionDAO, CasOutHandler casOutHandler) { - AuthorizingRealm bean = new AuthorizingRealm(); - bean.setCachingEnabled(false); - bean.setSessionDAO(sessionDAO); - bean.setCasOutHandler(casOutHandler); - bean.setCasServerUrl(Global.getProperty("shiro.casServerUrl")); - bean.setCasServerCallbackUrl(Global.getProperty("shiro.casClientUrl") + Global.getAdminPath() + "/login-cas"); - return bean; - } - /** * CAS登录过滤器 */ - private CasAuthenticationFilter shiroCasFilter(AuthorizingRealm authorizingRealm) { + private CasAuthenticationFilter shiroCasFilter(CasAuthorizingRealm casAuthorizingRealm) { CasAuthenticationFilter bean = new CasAuthenticationFilter(); - bean.setAuthorizingRealm(authorizingRealm); + bean.setAuthorizingRealm(casAuthorizingRealm); return bean; } @@ -83,8 +65,10 @@ public class ShiroConfig { /** * 登出过滤器 */ - private LogoutFilter shiroLogoutFilter() { - return new LogoutFilter(); + private LogoutFilter shiroLogoutFilter(AuthorizingRealm authorizingRealm) { + LogoutFilter bean = new LogoutFilter(); + bean.setAuthorizingRealm(authorizingRealm); + return bean; } /** @@ -113,15 +97,15 @@ public class ShiroConfig { */ @Bean public ShiroFilterFactoryBean shiroFilter(WebSecurityManager securityManager, - AuthorizingRealm authorizingRealm) { + AuthorizingRealm authorizingRealm, CasAuthorizingRealm casAuthorizingRealm) { ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); bean.setSecurityManager(securityManager); bean.setLoginUrl(Global.getProperty("shiro.loginUrl")); bean.setSuccessUrl(Global.getProperty("shiro.successUrl")); Map filters = bean.getFilters(); - filters.put("cas", shiroCasFilter(authorizingRealm)); + filters.put("cas", shiroCasFilter(casAuthorizingRealm)); filters.put("authc", shiroAuthcFilter(authorizingRealm)); - filters.put("logout", shiroLogoutFilter()); + filters.put("logout", shiroLogoutFilter(authorizingRealm)); filters.put("perms", shiroPermsFilter()); filters.put("roles", shiroRolesFilter()); filters.put("user", shiroUserFilter()); @@ -131,21 +115,57 @@ public class ShiroConfig { bean.setFilterChainDefinitionMap(chains.getObject()); return bean; } + + /** + * 系统安全认证实现类 + */ + @Bean + public AuthorizingRealm authorizingRealm(SessionDAO sessionDAO) { + AuthorizingRealm bean = new AuthorizingRealm(); + bean.setSessionDAO(sessionDAO); + return bean; + } + + /** + * 单点登录信息句柄,单点退出用 + */ + @Bean + public CasOutHandler casOutHandler() { + return new CasOutHandler(); + } + + /** + * 系统安全认证实现类 + */ + @Bean + public CasAuthorizingRealm casAuthorizingRealm(SessionDAO sessionDAO, CasOutHandler casOutHandler) { + CasAuthorizingRealm bean = new CasAuthorizingRealm(); + bean.setSessionDAO(sessionDAO); + bean.setCasOutHandler(casOutHandler); + bean.setCasServerUrl(Global.getProperty("shiro.casServerUrl")); + bean.setCasServerCallbackUrl(Global.getProperty("shiro.casClientUrl") + Global.getAdminPath() + "/login-cas"); + return bean; + } /** * 定义Shiro安全管理配置 */ @Bean - public WebSecurityManager securityManager(AuthorizingRealm authorizingRealm, SessionManager sessionManager, CacheManager shiroCacheManager) { + public WebSecurityManager securityManager(AuthorizingRealm authorizingRealm, + CasAuthorizingRealm casAuthorizingRealm, SessionManager sessionManager, + CacheManager shiroCacheManager) { WebSecurityManager bean = new WebSecurityManager(); - bean.setRealm(authorizingRealm); + Collection realms = ListUtils.newArrayList(); + realms.add(authorizingRealm); // 第一个为权限授权控制类 + realms.add(casAuthorizingRealm); + bean.setRealms(realms); bean.setSessionManager(sessionManager); bean.setCacheManager(shiroCacheManager); // 设置支持CAS的subjectFactory bean.setSubjectFactory(new CasSubjectFactory()); return bean; } - + /** * Shiro 生命周期处理器,实现初始化和销毁回调 */ diff --git a/modules/core/src/main/java/com/jeesite/modules/sys/web/LoginController.java b/modules/core/src/main/java/com/jeesite/modules/sys/web/LoginController.java index 195c6dae..c3a6dbd6 100644 --- a/modules/core/src/main/java/com/jeesite/modules/sys/web/LoginController.java +++ b/modules/core/src/main/java/com/jeesite/modules/sys/web/LoginController.java @@ -229,7 +229,7 @@ public class LoginController extends BaseController{ model.addAttribute("user", user); // 设置当前用户信息 // 登录成功后,验证码计算器清零 - BaseAuthorizingRealm.isValidCodeLogin(loginInfo.getId(), /*loginInfo.getParam("corpCode"), */loginInfo.getParam("deviceType"), "success"); + BaseAuthorizingRealm.isValidCodeLogin(loginInfo.getId(), loginInfo.getParam("deviceType"), "success"); //获取当前会话对象 Session session = UserUtils.getSession(); diff --git a/modules/core/src/main/resources/config/jeesite-core.yml b/modules/core/src/main/resources/config/jeesite-core.yml index 32916a12..edb48ee5 100644 --- a/modules/core/src/main/resources/config/jeesite-core.yml +++ b/modules/core/src/main/resources/config/jeesite-core.yml @@ -58,6 +58,7 @@ jdbc: # Redis 配置 redis: + enabled: true # Redis 连接参数 host: 127.0.0.1 @@ -202,14 +203,14 @@ shiro: logoutUrl: ${shiro.loginUrl} successUrl: ${adminPath}/index - # CAS 相关配置 -# casServerUrl: http://192.168.1.3:8080/cas -# casClientUrl: http://192.168.1.3:8180/jeesite +# # Jasig CAS 相关配置 +# casServerUrl: http://127.0.0.1:8981/cas +# casClientUrl: http://127.0.0.1:8980/js # loginUrl: ${shiro.casServerUrl}?service=${shiro.casClientUrl}${adminPath}/login-cas # logoutUrl: ${shiro.casServerUrl}/logout?service=${shiro.loginUrl} # successUrl: ${shiro.casClientUrl}${adminPath}/index - # SSO 登录相关配置 + # 简单 SSO 登录相关配置 sso: # 如果启用/sso/{username}/{token}单点登录,请修改此安全key并与单点登录系统key一致。 diff --git a/modules/core/src/main/resources/i18n/core/common/i18n_en.properties b/modules/core/src/main/resources/i18n/core/common/i18n_en.properties index bd5ab183..97610765 100644 --- a/modules/core/src/main/resources/i18n/core/common/i18n_en.properties +++ b/modules/core/src/main/resources/i18n/core/common/i18n_en.properties @@ -10,7 +10,6 @@ sys.logout.success=Logout successful! # =========== 账号登录相关 =========== -sys.login.typeUnknown=Unknown login type. sys.login.accountIsBlank=Login account cannot be empty. sys.login.validCodeError=Login verification code error. sys.login.accountDisabled=This Account has disabled. diff --git a/modules/core/src/main/resources/i18n/core/common/i18n_zh_CN.properties b/modules/core/src/main/resources/i18n/core/common/i18n_zh_CN.properties index 601055e8..308883e9 100644 --- a/modules/core/src/main/resources/i18n/core/common/i18n_zh_CN.properties +++ b/modules/core/src/main/resources/i18n/core/common/i18n_zh_CN.properties @@ -10,7 +10,6 @@ sys.logout.success=退出成功! # =========== 账号登录相关 =========== -sys.login.typeUnknown=未知的登录类型。 sys.login.accountIsBlank=登录账号不能为空。 sys.login.validCodeError=登录验证码错误,请重试。 sys.login.accountDisabled=该帐号已停用。 diff --git a/web/src/main/resources/config/jeesite.yml b/web/src/main/resources/config/jeesite.yml index 4450bbcb..d24ab2c8 100644 --- a/web/src/main/resources/config/jeesite.yml +++ b/web/src/main/resources/config/jeesite.yml @@ -104,6 +104,7 @@ jdbc: # Redis 配置 #redis: +# enabled: true # # # Redis 连接参数 # host: 127.0.0.1 @@ -129,13 +130,13 @@ jdbc: #===== System settings ======# #============================# -##管理基础路径 +# 管理基础路径 #adminPath: /a # -##前端基础路径 +# 前端基础路径 #frontPath: /f # -## 分页配置 +# 分页配置 #page: # # # 分页默认大小 @@ -225,7 +226,7 @@ jdbc: #==== Framework settings ====# #============================# -## Shiro 相关配置 +# Shiro 相关配置 #shiro: # # #索引页路径 @@ -236,14 +237,14 @@ jdbc: # logoutUrl: ${shiro.loginUrl} # successUrl: ${adminPath}/index # -# # CAS 相关配置 -## casServerUrl: http://192.168.1.3:8080/cas -## casClientUrl: http://192.168.1.3:8180/jeesite +## # Jasig CAS 相关配置 +## casServerUrl: http://127.0.0.1:8981/cas +## casClientUrl: http://127.0.0.1:8980/js ## loginUrl: ${shiro.casServerUrl}?service=${shiro.casClientUrl}${adminPath}/login-cas ## logoutUrl: ${shiro.casServerUrl}/logout?service=${shiro.loginUrl} ## successUrl: ${shiro.casClientUrl}${adminPath}/index # -# # SSO 登录相关配置 +# # 简单 SSO 登录相关配置 # sso: # # # 如果启用/sso/{username}/{token}单点登录,请修改此安全key并与单点登录系统key一致。 @@ -279,7 +280,7 @@ jdbc: # /ReportServer/** = user # ${adminPath}/** = user # -## Session 相关 +# Session 相关 #session: # # #全局会话超时,单位:毫秒, 20m=1200000ms, 30m=1800000ms, 60m=3600000ms, 12h=43200000ms, 1day=86400000ms @@ -297,7 +298,7 @@ jdbc: # #共享的SessionId的Cookie名称,保存到跟路径下,第三方应用获取。同一域名下多个项目时需设置共享Cookie的名称。 # #shareSessionIdCookieName: ${session.sessionIdCookieName} # -## MyBatis 相关 +# MyBatis 相关 #mybatis: # # # 扫描基础包设置(Aliases、@MyBatisDao),如果多个,用“,”分隔 @@ -311,7 +312,7 @@ jdbc: # sleepSeconds: 3 # mappingPath: mappings # -## 缓存设置 +# 缓存设置 #ehcache: # # # 缓存配置文件路径 @@ -326,7 +327,7 @@ jdbc: # enabled: false # urlPatterns: "*.html" # -## Web 相关 +# Web 相关 #web: # # # MVC 视图相关 @@ -368,7 +369,7 @@ jdbc: # # 静态文件后缀,排除的url路径,指定哪些uri路径不进行静态文件过滤。 # staticFileExcludeUri: /druid/ # -## 错误页面500.html是否输出错误信息(正式环境,为提供安全性可设置为false) +# 错误页面500.html是否输出错误信息(正式环境,为提供安全性可设置为false) #error: # page: # printErrorInfo: true