项目需求、任务以及模块精简
This commit is contained in:
@@ -122,13 +122,6 @@
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- CAS 单点登录模块 -->
|
||||
<dependency>
|
||||
<groupId>com.jeesite</groupId>
|
||||
<artifactId>jeesite-module-cas</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- ELK 日志收集 -->
|
||||
<dependency>
|
||||
<groupId>net.logstash.logback</groupId>
|
||||
|
||||
@@ -6,12 +6,9 @@ package com.jeesite.autoconfigure.core;
|
||||
|
||||
import com.jeesite.common.collect.ListUtils;
|
||||
import com.jeesite.common.config.Global;
|
||||
import com.jeesite.common.shiro.cas.CasOutHandler;
|
||||
import com.jeesite.common.shiro.cas.CasSubjectFactory;
|
||||
import com.jeesite.common.shiro.config.FilterChainDefinitionMap;
|
||||
import com.jeesite.common.shiro.filter.*;
|
||||
import com.jeesite.common.shiro.realm.AuthorizingRealm;
|
||||
import com.jeesite.common.shiro.realm.CasAuthorizingRealm;
|
||||
import com.jeesite.common.shiro.realm.LdapAuthorizingRealm;
|
||||
import com.jeesite.common.shiro.session.SessionDAO;
|
||||
import com.jeesite.common.shiro.session.SessionManager;
|
||||
@@ -68,15 +65,6 @@ public class ShiroAutoConfiguration {
|
||||
return new InnerFilter();
|
||||
}
|
||||
|
||||
/**
|
||||
* CAS登录过滤器
|
||||
*/
|
||||
private CasFilter shiroCasFilter(CasAuthorizingRealm casAuthorizingRealm) {
|
||||
CasFilter bean = new CasFilter();
|
||||
bean.setAuthorizingRealm(casAuthorizingRealm);
|
||||
return bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* LDAP登录过滤器
|
||||
*/
|
||||
@@ -140,14 +128,13 @@ public class ShiroAutoConfiguration {
|
||||
@Bean("shiroFilter")
|
||||
@ConditionalOnMissingBean(name="shiroFilter")
|
||||
public ShiroFilterFactoryBean shiroFilter(WebSecurityManager webSecurityManager, AuthorizingRealm authorizingRealm,
|
||||
CasAuthorizingRealm casAuthorizingRealm, LdapAuthorizingRealm ldapAuthorizingRealm) {
|
||||
LdapAuthorizingRealm ldapAuthorizingRealm) {
|
||||
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
|
||||
bean.setSecurityManager(webSecurityManager);
|
||||
bean.setLoginUrl(Global.getProperty("shiro.loginUrl"));
|
||||
bean.setSuccessUrl(Global.getProperty("adminPath")+"/index");
|
||||
Map<String, Filter> filters = bean.getFilters();
|
||||
filters.put("inner", shiroInnerFilter());
|
||||
filters.put("cas", shiroCasFilter(casAuthorizingRealm));
|
||||
filters.put("ldap", shiroLdapFilter(ldapAuthorizingRealm));
|
||||
filters.put("authc", shiroAuthcFilter(authorizingRealm));
|
||||
filters.put("logout", shiroLogoutFilter(authorizingRealm));
|
||||
@@ -173,35 +160,12 @@ public class ShiroAutoConfiguration {
|
||||
return bean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 单点登录信息句柄,单点退出用
|
||||
*/
|
||||
@Bean("casOutHandler")
|
||||
@ConditionalOnMissingBean(name="casOutHandler")
|
||||
public CasOutHandler casOutHandler() {
|
||||
return new CasOutHandler();
|
||||
}
|
||||
|
||||
/**
|
||||
* CAS安全认证实现类
|
||||
*/
|
||||
@Bean("casAuthorizingRealm")
|
||||
@ConditionalOnMissingBean(name="casAuthorizingRealm")
|
||||
public CasAuthorizingRealm casAuthorizingRealm(@Qualifier("sessionDAO") 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* LDAP安全认证实现类
|
||||
*/
|
||||
@Bean("ldapAuthorizingRealm")
|
||||
@ConditionalOnMissingBean(name="ldapAuthorizingRealm")
|
||||
public LdapAuthorizingRealm ldapAuthorizingRealm(@Qualifier("sessionDAO") SessionDAO sessionDAO, CasOutHandler casOutHandler) {
|
||||
public LdapAuthorizingRealm ldapAuthorizingRealm(@Qualifier("sessionDAO") SessionDAO sessionDAO) {
|
||||
LdapAuthorizingRealm bean = new LdapAuthorizingRealm();
|
||||
JndiLdapContextFactory contextFactory = (JndiLdapContextFactory) bean.getContextFactory();
|
||||
contextFactory.setUrl(Global.getProperty("shiro.ldapUrl"/*, "ldap://127.0.0.1:389"*/));
|
||||
@@ -215,17 +179,15 @@ public class ShiroAutoConfiguration {
|
||||
*/
|
||||
@Bean("webSecurityManager")
|
||||
@ConditionalOnMissingBean(name="webSecurityManager")
|
||||
public WebSecurityManager webSecurityManager(AuthorizingRealm authorizingRealm, CasAuthorizingRealm casAuthorizingRealm,
|
||||
public WebSecurityManager webSecurityManager(AuthorizingRealm authorizingRealm,
|
||||
LdapAuthorizingRealm ldapAuthorizingRealm, SessionManager sessionManager, @Qualifier("shiroCacheManager") CacheManager shiroCacheManager) {
|
||||
WebSecurityManager bean = new WebSecurityManager();
|
||||
Collection<Realm> realms = ListUtils.newArrayList();
|
||||
realms.add(authorizingRealm); // 第一个为权限授权控制类
|
||||
realms.add(casAuthorizingRealm);
|
||||
realms.add(ldapAuthorizingRealm);
|
||||
bean.setRealms(realms);
|
||||
bean.setSessionManager(sessionManager);
|
||||
bean.setCacheManager(shiroCacheManager);
|
||||
bean.setSubjectFactory(new CasSubjectFactory());
|
||||
//bean.setRememberMeManager(null); // 关闭 RememberMe
|
||||
return bean;
|
||||
}
|
||||
|
||||
@@ -1,81 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2013-Now https://jeesite.com All rights reserved.
|
||||
* No deletion without permission, or be held responsible to law.
|
||||
*/
|
||||
package com.jeesite.common.shiro.filter;
|
||||
|
||||
import com.jeesite.common.config.Global;
|
||||
import com.jeesite.common.lang.ExceptionUtils;
|
||||
import com.jeesite.common.lang.StringUtils;
|
||||
import com.jeesite.common.shiro.cas.CasBaseFilter;
|
||||
import com.jeesite.common.shiro.realm.BaseAuthorizingRealm;
|
||||
import com.jeesite.common.web.http.ServletUtils;
|
||||
import com.jeesite.modules.sys.utils.UserUtils;
|
||||
import org.apache.shiro.authc.AuthenticationException;
|
||||
import org.apache.shiro.authc.AuthenticationToken;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
import org.apache.shiro.web.util.WebUtils;
|
||||
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* CAS过滤器
|
||||
* @author ThinkGem
|
||||
* @version 2020-9-19
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class CasFilter extends CasBaseFilter {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(CasFilter.class);
|
||||
private BaseAuthorizingRealm authorizingRealm;
|
||||
|
||||
public CasFilter() {
|
||||
this.setSuccessUrl(Global.getProperty("shiro.successUrl"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录成功调用事件
|
||||
*/
|
||||
@Override
|
||||
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
|
||||
authorizingRealm.onLoginSuccess(UserUtils.getLoginInfo(subject), (HttpServletRequest)request);
|
||||
ServletUtils.redirectUrl((HttpServletRequest)request, (HttpServletResponse)response, getSuccessUrl());
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录失败调用事件
|
||||
*/
|
||||
@Override
|
||||
protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException ae, ServletRequest request, ServletResponse response) {
|
||||
Subject subject = getSubject(request, response);
|
||||
if (subject.isAuthenticated() || subject.isRemembered()) {
|
||||
ServletUtils.redirectUrl((HttpServletRequest)request, (HttpServletResponse)response, getSuccessUrl());
|
||||
return false;
|
||||
} else {
|
||||
try {
|
||||
String message = ExceptionUtils.getExceptionMessage(ae);
|
||||
if (StringUtils.isNotBlank(message)){
|
||||
request.setAttribute("exception", ae);
|
||||
request.setAttribute("message", message);
|
||||
request.getRequestDispatcher("/error/403").forward(request, response);
|
||||
}else{
|
||||
WebUtils.issueRedirect(request, response, getLoginUrl());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error(e.getMessage(), e);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void setAuthorizingRealm(BaseAuthorizingRealm authorizingRealm) {
|
||||
this.authorizingRealm = authorizingRealm;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,217 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) 2013-Now https://jeesite.com All rights reserved.
|
||||
* No deletion without permission, or be held responsible to law.
|
||||
*/
|
||||
package com.jeesite.common.shiro.realm;
|
||||
|
||||
import com.jeesite.common.codec.EncodeUtils;
|
||||
import com.jeesite.common.collect.MapUtils;
|
||||
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.shiro.cas.CasToken;
|
||||
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.utils.LogUtils;
|
||||
import com.jeesite.modules.sys.utils.UserUtils;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.validation.ValidationException;
|
||||
import org.apache.shiro.authc.AuthenticationException;
|
||||
import org.apache.shiro.authc.AuthenticationInfo;
|
||||
import org.apache.shiro.authc.AuthenticationToken;
|
||||
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 java.util.Map;
|
||||
|
||||
/**
|
||||
* 系统认证授权实现类
|
||||
* @author ThinkGem
|
||||
* @version 2020-9-19
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public class CasAuthorizingRealm extends BaseAuthorizingRealm {
|
||||
|
||||
private CasOutHandler casOutHandler;
|
||||
private String casServerUrl; // CAS 服务器地址
|
||||
private String casServerCallbackUrl; // CAS 服务器回调地址
|
||||
private TicketValidator ticketValidator;// CAS 令牌验证类
|
||||
|
||||
private EmpUserService empUserService;
|
||||
|
||||
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(EncodeUtils.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(request);
|
||||
token.setUsername(casPrincipal.getName());
|
||||
Map<String, Object> params = MapUtils.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<String, Object> 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.setCorpCode_(EncodeUtils.decodeUrl(ObjectUtils.toString(attrs.get("corpCode"))));
|
||||
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(), user.getCorpCode_());
|
||||
if (user != null) {
|
||||
return user;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 其它类型,根据项目需要自行创建
|
||||
else{
|
||||
User finalUser = user;
|
||||
SpringUtils.getBeanIfAvailable(CasCreateUser.class, (casCreateUser) -> {
|
||||
casCreateUser.createUser(finalUser, attrs);
|
||||
}, (e -> {
|
||||
throw new AuthenticationException("msg:用户 “" + token.getUsername()
|
||||
+ "”, 类型 “" + finalUser.getUserType() + "” 在本系统中不存在, 请联系管理员.");
|
||||
}));
|
||||
}
|
||||
}else{
|
||||
throw new AuthenticationException("msg:用户 “" + token.getUsername() + "” 在本系统中不存在, 请联系管理员.");
|
||||
}
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void assertCredentialsMatch(AuthenticationToken authcToken,
|
||||
AuthenticationInfo info) throws AuthenticationException {
|
||||
// 已经在 getFormToken 认证过了,这里就不验证身份了
|
||||
}
|
||||
|
||||
@Override
|
||||
public User onLoginSuccess(LoginInfo loginInfo, HttpServletRequest request) {
|
||||
User user = 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());
|
||||
|
||||
// 记录用户登录日志
|
||||
LogUtils.saveLog(user, request, "系统登录", Log.TYPE_LOGIN_LOGOUT);
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public User onLogoutSuccess(LoginInfo loginInfo, HttpServletRequest request) {
|
||||
User user = super.onLogoutSuccess(loginInfo, request);
|
||||
|
||||
// 记录用户退出日志
|
||||
LogUtils.saveLog(user, request, "系统退出", Log.TYPE_LOGIN_LOGOUT);
|
||||
return user;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -570,7 +570,6 @@ shiro:
|
||||
/bpm/modeler/** = perms[bpm:modeler]
|
||||
/ureport/designer/** = perms[ureport]
|
||||
/ureport/datasource/** = perms[ureport]
|
||||
${adminPath}/login-cas = cas
|
||||
${adminPath}/login-ldap = ldap
|
||||
${adminPath}/login = authc
|
||||
${adminPath}/logout = logout
|
||||
|
||||
Reference in New Issue
Block a user