单点登录接口和无条件登录接口优化改进,并在调用登录时统一增加登录成功和失败事件,方便返回登录信息。

This commit is contained in:
thinkgem
2020-09-20 09:49:22 +08:00
parent f3bb96d719
commit bbb37722d6
5 changed files with 101 additions and 74 deletions

View File

@@ -3,44 +3,34 @@
*/ */
package com.jeesite.common.shiro.filter; package com.jeesite.common.shiro.filter;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest; import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject; import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.util.WebUtils; import org.apache.shiro.web.util.WebUtils;
import com.jeesite.common.lang.ExceptionUtils;
import com.jeesite.common.lang.StringUtils; import com.jeesite.common.lang.StringUtils;
import com.jeesite.common.shiro.realm.CasAuthorizingRealm; import com.jeesite.common.shiro.realm.CasAuthorizingRealm;
import com.jeesite.common.shiro.realm.LoginInfo;
/** /**
* CAS过滤器 * CAS过滤器
* @author ThinkGem * @author ThinkGem
* @version 2018-7-11 * @version 2020-9-19
*/ */
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public class CasAuthenticationFilter extends org.apache.shiro.cas.CasFilter { public class CasAuthenticationFilter extends org.apache.shiro.cas.CasFilter {
private CasAuthorizingRealm authorizingRealm; // 安全认证类
/** /**
* 登录成功调用事件 * 登录成功调用事件
*/ */
@Override @Override
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception { protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
return FormAuthenticationFilter.onLoginSuccess((HttpServletRequest)request, (HttpServletResponse)response);
// 登录成功后初始化授权信息并处理登录后的操作
authorizingRealm.onLoginSuccess((LoginInfo)subject.getPrincipal(), (HttpServletRequest)request);
// AJAX不支持Redirect改用Forward
request.getRequestDispatcher(getSuccessUrl()).forward(request, response);
return false;
} }
/** /**
@@ -59,15 +49,15 @@ public class CasAuthenticationFilter extends org.apache.shiro.cas.CasFilter {
return false; return false;
} else { } else {
try { try {
if (ae != null && StringUtils.startsWith(ae.getMessage(), "msg:")){ String message = ExceptionUtils.getExceptionMessage(ae);
if (StringUtils.isNotBlank(message)){
request.setAttribute("exception", ae); request.setAttribute("exception", ae);
request.setAttribute("message", message);
request.getRequestDispatcher("/error/403").forward(request, response); request.getRequestDispatcher("/error/403").forward(request, response);
}else{ }else{
WebUtils.issueRedirect(request, response, getLoginUrl()); WebUtils.issueRedirect(request, response, getLoginUrl());
} }
} catch (ServletException e) { } catch (Exception e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
return false; return false;
@@ -75,7 +65,7 @@ public class CasAuthenticationFilter extends org.apache.shiro.cas.CasFilter {
} }
public void setAuthorizingRealm(CasAuthorizingRealm authorizingRealm) { public void setAuthorizingRealm(CasAuthorizingRealm authorizingRealm) {
this.authorizingRealm = authorizingRealm;
} }
} }

View File

@@ -34,7 +34,6 @@ import com.jeesite.common.lang.StringUtils;
import com.jeesite.common.network.IpUtils; import com.jeesite.common.network.IpUtils;
import com.jeesite.common.shiro.authc.FormToken; import com.jeesite.common.shiro.authc.FormToken;
import com.jeesite.common.shiro.realm.BaseAuthorizingRealm; import com.jeesite.common.shiro.realm.BaseAuthorizingRealm;
import com.jeesite.common.shiro.realm.LoginInfo;
import com.jeesite.common.web.CookieUtils; import com.jeesite.common.web.CookieUtils;
import com.jeesite.common.web.http.ServletUtils; import com.jeesite.common.web.http.ServletUtils;
import com.jeesite.modules.sys.entity.Log; import com.jeesite.modules.sys.entity.Log;
@@ -45,20 +44,20 @@ import com.jeesite.modules.sys.utils.UserUtils;
/** /**
* 表单验证(包含验证码)过滤类 * 表单验证(包含验证码)过滤类
* @author ThinkGem * @author ThinkGem
* @version 2020-4-13 * @version 2020-9-19
*/ */
public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.FormAuthenticationFilter { public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.FormAuthenticationFilter {
public static final String CAPTCHA_PARAM = "validCode"; // 验证码 public static final String CAPTCHA_PARAM = "validCode"; // 验证码
public static final String MESSAGE_PARAM = "message"; // 登录返回消息 public static final String MESSAGE_PARAM = "message"; // 登录返回消息
public static final String REMEMBER_USERCODE_PARAM = "rememberUserCode"; // 记住用户名 public static final String REMEMBER_USERCODE_PARAM = "rememberUserCode"; // 记住用户名
public static final String EXCEPTION_ATTRIBUTE_NAME = "exception"; // 异常类属性名 public static final String EXCEPTION_ATTRIBUTE_NAME = "exception"; // 异常类属性名
private static final Logger logger = LoggerFactory.getLogger(FormAuthenticationFilter.class); private static final Logger logger = LoggerFactory.getLogger(FormAuthenticationFilter.class);
private static FormAuthenticationFilter instance;
private BaseAuthorizingRealm authorizingRealm; // 安全认证类
private Cookie rememberUserCodeCookie; // 记住用户名Cookie private BaseAuthorizingRealm authorizingRealm;
private Cookie rememberUserCodeCookie; // 记住用户名Cookie
/** /**
* 构造方法 * 构造方法
@@ -68,8 +67,16 @@ public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.
rememberUserCodeCookie = new SimpleCookie(REMEMBER_USERCODE_PARAM); rememberUserCodeCookie = new SimpleCookie(REMEMBER_USERCODE_PARAM);
rememberUserCodeCookie.setHttpOnly(true); rememberUserCodeCookie.setHttpOnly(true);
rememberUserCodeCookie.setMaxAge(Cookie.ONE_YEAR); rememberUserCodeCookie.setMaxAge(Cookie.ONE_YEAR);
instance = this;
} }
/**
* 创建登录授权令牌
*/
public static FormToken newToken(HttpServletRequest request, HttpServletResponse response) {
return (FormToken)instance.createToken(request, response);
}
/** /**
* 创建登录授权令牌 * 创建登录授权令牌
*/ */
@@ -100,7 +107,7 @@ public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.
logger.info("登录账号为空或解码错误."); logger.info("登录账号为空或解码错误.");
} }
} }
// 登录成功后,判断是否需要记住用户名 // 登录判断是否需要记住用户名
if (WebUtils.isTrue(request, REMEMBER_USERCODE_PARAM)) { if (WebUtils.isTrue(request, REMEMBER_USERCODE_PARAM)) {
rememberUserCodeCookie.setValue(EncodeUtils.encodeUrl(EncodeUtils.xssFilter(username))); rememberUserCodeCookie.setValue(EncodeUtils.encodeUrl(EncodeUtils.xssFilter(username)));
rememberUserCodeCookie.saveTo((HttpServletRequest)request, (HttpServletResponse)response); rememberUserCodeCookie.saveTo((HttpServletRequest)request, (HttpServletResponse)response);
@@ -241,30 +248,51 @@ public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.
return super.executeLogin(request, response); return super.executeLogin(request, response);
} }
/**
* 登录成功调用事件(静态方便其他位置调用)
*/
public static boolean onLoginSuccess(HttpServletRequest request, HttpServletResponse response) {
try {
return instance.onLoginSuccess(null, null, request, response);
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
/** /**
* 登录成功调用事件 * 登录成功调用事件
*/ */
@Override @Override
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception { protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {
// 登录成功后初始化授权信息并处理登录后的操作 // 登录成功后初始化授权信息并处理登录后的操作
authorizingRealm.onLoginSuccess((LoginInfo)subject.getPrincipal(), (HttpServletRequest) request); authorizingRealm.onLoginSuccess(UserUtils.getLoginInfo(), (HttpServletRequest)request);
// AJAX不支持Redirect改用Forward // AJAX不支持Redirect改用Forward
request.getRequestDispatcher(getSuccessUrl()).forward(request, response); try {
request.getRequestDispatcher(getSuccessUrl()).forward(request, response);
} catch (Exception e) {
e.printStackTrace();
}
return false; return false;
} }
/**
* 登录失败调用事件(静态方便其他位置调用)
*/
public static boolean onLoginFailure(AuthenticationException e, HttpServletRequest request, HttpServletResponse response) {
return instance.onLoginFailure(null, e, request, response);
}
/** /**
* 登录失败调用事件 * 登录失败调用事件
*/ */
@Override @Override
protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) { protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {
String message = StringUtils.EMPTY; String message = StringUtils.EMPTY;
if (e instanceof IncorrectCredentialsException || e instanceof UnknownAccountException) { if (e.getMessage() != null && StringUtils.startsWith(e.getMessage(), "msg:")) {
message = Global.getText("sys.login.failure");
} else if (e.getMessage() != null && StringUtils.startsWith(e.getMessage(), "msg:")) {
message = StringUtils.replace(e.getMessage(), "msg:", ""); message = StringUtils.replace(e.getMessage(), "msg:", "");
} else if (e instanceof IncorrectCredentialsException || e instanceof UnknownAccountException) {
message = Global.getText("sys.login.failure");
} else { } else {
message = Global.getText("sys.login.error"); message = Global.getText("sys.login.error");
logger.error(message, e); // 输出到日志文件 logger.error(message, e); // 输出到日志文件
@@ -272,20 +300,20 @@ public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.
request.setAttribute(EXCEPTION_ATTRIBUTE_NAME, e); request.setAttribute(EXCEPTION_ATTRIBUTE_NAME, e);
request.setAttribute(MESSAGE_PARAM, message); request.setAttribute(MESSAGE_PARAM, message);
// 登录操作如果是Ajax操作直接返回登录信息字符串。 // AJAX不支持Redirect改用Forward
if (ServletUtils.isAjaxRequest(((HttpServletRequest) request))){ try {
Map<String, Object> data = getLoginFailureData(((HttpServletRequest) request), ((HttpServletResponse) response)); String loginFailureUrl = Global.getProperty("adminPath")+"/loginFailure";
ServletUtils.renderResult(((HttpServletResponse) response), Global.TRUE, message, data); request.getRequestDispatcher(loginFailureUrl).forward(request, response);
return false; } catch (Exception ex) {
ex.printStackTrace();
} }
return false;
return true;
}
public void setAuthorizingRealm(BaseAuthorizingRealm authorizingRealm) {
this.authorizingRealm = authorizingRealm;
} }
/**
* 获取登录页面数据
* @author ThinkGem
*/
public static Map<String, Object> getLoginData(HttpServletRequest request, HttpServletResponse response) { public static Map<String, Object> getLoginData(HttpServletRequest request, HttpServletResponse response) {
Map<String, Object> data = MapUtils.newHashMap(); Map<String, Object> data = MapUtils.newHashMap();
@@ -294,7 +322,7 @@ public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.
for (Entry<String, Object> entry : paramMap.entrySet()){ for (Entry<String, Object> entry : paramMap.entrySet()){
data.put(ServletUtils.EXT_PARAMS_PREFIX + entry.getKey(), entry.getValue()); data.put(ServletUtils.EXT_PARAMS_PREFIX + entry.getKey(), entry.getValue());
} }
// 如果已登录,再次访问主页,则退出原账号。 // 如果已登录,再次访问主页,则退出原账号。
if (!Global.TRUE.equals(Global.getConfig("shiro.isAllowRefreshIndex"))){ if (!Global.TRUE.equals(Global.getConfig("shiro.isAllowRefreshIndex"))){
CookieUtils.setCookie(response, "LOGINED", "false"); CookieUtils.setCookie(response, "LOGINED", "false");
@@ -316,6 +344,10 @@ public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.
return data; return data;
} }
/**
* 获取登录失败数据
* @author ThinkGem
*/
public static Map<String, Object> getLoginFailureData(HttpServletRequest request, HttpServletResponse response) { public static Map<String, Object> getLoginFailureData(HttpServletRequest request, HttpServletResponse response) {
Map<String, Object> data = MapUtils.newHashMap(); Map<String, Object> data = MapUtils.newHashMap();
@@ -362,4 +394,9 @@ public class FormAuthenticationFilter extends org.apache.shiro.web.filter.authc.
data.put("result", Global.FALSE); data.put("result", Global.FALSE);
return data; return data;
} }
public void setAuthorizingRealm(BaseAuthorizingRealm authorizingRealm) {
this.authorizingRealm = authorizingRealm;
}
} }

View File

@@ -38,7 +38,7 @@ import com.jeesite.modules.sys.utils.UserUtils;
/** /**
* 系统安全认证实现类 * 系统安全认证实现类
* @author ThinkGem * @author ThinkGem
* @version 2018-7-11 * @version 2020-9-19
*/ */
@SuppressWarnings("deprecation") @SuppressWarnings("deprecation")
public class CasAuthorizingRealm extends BaseAuthorizingRealm { public class CasAuthorizingRealm extends BaseAuthorizingRealm {
@@ -92,7 +92,7 @@ public class CasAuthorizingRealm extends BaseAuthorizingRealm {
casToken.setUserId(casPrincipal.getName()); casToken.setUserId(casPrincipal.getName());
// 生成登录信息对象 // 生成登录信息对象
FormToken token = new FormToken(); FormToken token = new FormToken(request);
token.setUsername(casPrincipal.getName()); token.setUsername(casPrincipal.getName());
Map<String, Object> params = MapUtils.newHashMap(); Map<String, Object> params = MapUtils.newHashMap();
params.putAll(casPrincipal.getAttributes()); params.putAll(casPrincipal.getAttributes());

View File

@@ -37,14 +37,14 @@ import com.jeesite.modules.sys.utils.UserUtils;
/** /**
* 登录Controller * 登录Controller
* @author ThinkGem * @author ThinkGem
* @version 2020-4-13 * @version 2020-9-19
*/ */
@Controller @Controller
@RequestMapping(value = "${adminPath}") @RequestMapping(value = "${adminPath}")
public class LoginController extends BaseController{ public class LoginController extends BaseController{
/** /**
* 管理登录 * 登录页面
*/ */
@RequestMapping(value = "login", method = RequestMethod.GET) @RequestMapping(value = "login", method = RequestMethod.GET)
public String login(HttpServletRequest request, HttpServletResponse response, Model model) { public String login(HttpServletRequest request, HttpServletResponse response, Model model) {
@@ -94,9 +94,9 @@ public class LoginController extends BaseController{
} }
/** /**
* 登录失败,真正登录的POST请求由Filter完成 * 登录失败,返回错误信息
*/ */
@RequestMapping(value = "login", method = RequestMethod.POST) @RequestMapping(value = "loginFailure")
public String loginFailure(HttpServletRequest request, HttpServletResponse response, Model model) { public String loginFailure(HttpServletRequest request, HttpServletResponse response, Model model) {
LoginInfo loginInfo = UserUtils.getLoginInfo(); LoginInfo loginInfo = UserUtils.getLoginInfo();
@@ -200,6 +200,9 @@ public class LoginController extends BaseController{
// 获取登录成功后跳转的页面 // 获取登录成功后跳转的页面
String successUrl = request.getParameter("__url"); String successUrl = request.getParameter("__url");
if (StringUtils.isBlank(successUrl)){
successUrl = (String)request.getAttribute("__url");
}
if (StringUtils.isBlank(successUrl)){ if (StringUtils.isBlank(successUrl)){
successUrl = Global.getProperty("shiro.successUrl"); successUrl = Global.getProperty("shiro.successUrl");
} }
@@ -272,16 +275,16 @@ public class LoginController extends BaseController{
} }
/** /**
* 获取侧边栏菜单数据 * 侧边栏菜单数据
*/ */
@RequiresPermissions("user") @RequiresPermissions("user")
@RequestMapping(value = "index/menuTree") @RequestMapping(value = "index/menuTree")
public String indexMenuTree(String parentCode) { public String indexMenuTree(String parentCode) {
return "modules/sys/sysIndex/menuTree"; return "modules/sys/menuTree";
} }
/** /**
* 获取当前用户权限字符串数据(移动端用) * 当前用户权限字符串数据(移动端用)
*/ */
@RequiresPermissions("user") @RequiresPermissions("user")
@RequestMapping(value = "authInfo") @RequestMapping(value = "authInfo")
@@ -291,7 +294,7 @@ public class LoginController extends BaseController{
} }
/** /**
* 获取当前用户菜单数据(移动端用) * 当前用户菜单数据(移动端用)
*/ */
@RequiresPermissions("user") @RequiresPermissions("user")
@RequestMapping(value = "menuTree") @RequestMapping(value = "menuTree")
@@ -334,7 +337,7 @@ public class LoginController extends BaseController{
} }
/** /**
* 切换主题 * 切换主题风格
*/ */
@RequiresPermissions("user") @RequiresPermissions("user")
@RequestMapping(value = "switchSkin/{skinName}") @RequestMapping(value = "switchSkin/{skinName}")

View File

@@ -18,6 +18,7 @@ import com.jeesite.common.codec.EncodeUtils;
import com.jeesite.common.config.Global; import com.jeesite.common.config.Global;
import com.jeesite.common.lang.ObjectUtils; import com.jeesite.common.lang.ObjectUtils;
import com.jeesite.common.shiro.authc.FormToken; import com.jeesite.common.shiro.authc.FormToken;
import com.jeesite.common.shiro.filter.FormAuthenticationFilter;
import com.jeesite.common.web.BaseController; import com.jeesite.common.web.BaseController;
import com.jeesite.common.web.http.ServletUtils; import com.jeesite.common.web.http.ServletUtils;
import com.jeesite.modules.sys.entity.User; import com.jeesite.modules.sys.entity.User;
@@ -26,7 +27,7 @@ import com.jeesite.modules.sys.utils.UserUtils;
/** /**
* 单点登录Controller * 单点登录Controller
* @author ThinkGem * @author ThinkGem
* @version 2017-03-25 * @version 2020-9-19
*/ */
@Controller @Controller
public class SsoController extends BaseController{ public class SsoController extends BaseController{
@@ -63,24 +64,20 @@ public class SsoController extends BaseController{
// 通过令牌登录系统 // 通过令牌登录系统
if (token != null){ if (token != null){
try { try {
FormToken upToken = new FormToken(); // FormToken 构造方法的三个参数:登录名、单点登录的令牌秘钥、请求对象
upToken.setUsername(username); // 登录用户名 UserUtils.getSubject().login(new FormToken(username, token, request));
upToken.setSsoToken(token); // 单点登录令牌 request.setAttribute("__url", EncodeUtils.decodeUrl2(url));
upToken.setParams(ServletUtils.getExtParams(request)); // 登录附加参数 FormAuthenticationFilter.onLoginSuccess(request, response);
UserUtils.getSubject().login(upToken);
if (ServletUtils.isAjaxRequest(request)){
return ServletUtils.renderResult(response, Global.TRUE, text("账号登录成功"));
}else{
return REDIRECT + EncodeUtils.decodeUrl2(url);
}
} catch (AuthenticationException e) { } catch (AuthenticationException e) {
if (!e.getMessage().startsWith("msg:")){ FormAuthenticationFilter.onLoginFailure(e, request, response);
throw new AuthenticationException("msg:登录失败,请联系管理员。", e);
}
throw e;
} }
return null;
} }
return "error/403"; return "error/403";
} }
// public static void main(String[] args) {
// System.out.println(UserUtils.getSsoToken("system"));
// }
} }