一、第一个问题
看了很多文章,遇到的问题是:在自定义的认证处理器中抛出了自定义的异常,配置文件也已经配好自定义的异常,但是登录界面提示的错误信息依然是默认的AccountNotFountException配置的信息。通过debug发现了端倪,最后覆盖源码的AuthenticationExceptionHandlerAction.java就好了。
-
自定义异常
-
yml配置
-
国际化文件配置
-
在自定义认证器抛出异常
-
复制源码AuthenticationExceptionHandlerAction.java并修改覆盖源码
5.1建包:
5.2AuthenticationExceptionHandlerAction.java源码(70-85行左右)修改后的结果:
为什么这样修改?读者可以dubug自行调试这部分代码,这里不再介绍(如何调试请看标题一、6.)。
package org.apereo.cas.web.flow.actions;
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import java.net.URI;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import lombok.Generated;
import org.apereo.cas.authentication.AuthenticationException;
import org.apereo.cas.services.UnauthorizedServiceForPrincipalException;
import org.apereo.cas.ticket.AbstractTicketException;
import org.apereo.cas.web.support.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.binding.message.MessageBuilder;
import org.springframework.binding.message.MessageContext;
import org.springframework.webflow.action.AbstractAction;
import org.springframework.webflow.action.EventFactorySupport;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;
public class AuthenticationExceptionHandlerAction extends AbstractAction {
@Generated
private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticationExceptionHandlerAction.class);
private static final String DEFAULT_MESSAGE_BUNDLE_PREFIX = "authenticationFailure.";
private static final String UNKNOWN = "UNKNOWN";
private final Set<Class<? extends Throwable>> errors;
private String messageBundlePrefix;
public AuthenticationExceptionHandlerAction() {
this(new LinkedHashSet());
}
public AuthenticationExceptionHandlerAction(final Set<Class<? extends Throwable>> errors) {
this.messageBundlePrefix = "authenticationFailure.";
this.errors = errors;
}
public Set<Class<? extends Throwable>> getErrors() {
return new LinkedHashSet(this.errors);
}
public String handle(final Exception e, final RequestContext requestContext) {
MessageContext messageContext = requestContext.getMessageContext();
if (e instanceof AuthenticationException) {
return this.handleAuthenticationException((AuthenticationException)e, requestContext);
} else if (e instanceof AbstractTicketException) {
return this.handleAbstractTicketException((AbstractTicketException)e, requestContext);
} else {
LOGGER.trace("Unable to translate errors of the authentication exception [{}]. Returning [{}]", e, "UNKNOWN");
String messageCode = this.messageBundlePrefix + "UNKNOWN";
messageContext.addMessage((new MessageBuilder()).error().code(messageCode).build());
return "UNKNOWN";
}
}
protected String handleAuthenticationException(final AuthenticationException e, final RequestContext requestContext) {
if (e.getHandlerErrors().containsKey(UnauthorizedServiceForPrincipalException.class.getSimpleName())) {
URI url = WebUtils.getUnauthorizedRedirectUrlIntoFlowScope(requestContext);
if (url != null) {
LOGGER.warn("Unauthorized service access for principal; CAS will be redirecting to [{}]", url);
return "serviceUnauthorizedCheck";
}
}
Collection<Class> values = (Collection) e.getHandlerErrors().values().stream().map(Object::getClass).collect(Collectors.toList());
Objects.requireNonNull(values);
ArrayList<Class> tempList = new ArrayList(values);
Class myException = null;
for (Class clazz : tempList) {
// 自定义登录异常类,必须以My开头
if (clazz.getSimpleName().contains("My")) {
myException = clazz;
break;
}
}
if (myException == null) {
myException = tempList.get(0);
}
String handlerErrorName = myException.getSimpleName();
MessageContext messageContext = requestContext.getMessageContext();
String messageCode = this.messageBundlePrefix + handlerErrorName;
messageContext.addMessage((new MessageBuilder()).error().code(messageCode).build());
return handlerErrorName;
}
protected String handleAbstractTicketException(final AbstractTicketException e, final RequestContext requestContext) {
MessageContext messageContext = requestContext.getMessageContext();
Optional<String> match = this.errors.stream().filter((c) -> {
return c.isInstance(e);
}).map(Class::getSimpleName).findFirst();
match.ifPresent((s) -> {
messageContext.addMessage((new MessageBuilder()).error().code(e.getCode()).build());
});
return (String)match.orElse("UNKNOWN");
}
protected Event doExecute(final RequestContext requestContext) {
Event currentEvent = requestContext.getCurrentEvent();
LOGGER.debug("Located current event [{}]", currentEvent);
Exception error = (Exception)currentEvent.getAttributes().get("error", Exception.class);
if (error != null) {
LOGGER.debug("Located error attribute [{}] with message [{}] from the current event", error.getClass(), error.getMessage());
String event = this.handle(error, requestContext);
LOGGER.debug("Final event id resolved from the error is [{}]", event);
return (new EventFactorySupport()).event(this, event, currentEvent.getAttributes());
} else {
return (new EventFactorySupport()).event(this, "error");
}
}
}
5.3配置spring.factories扫描注册这个类到容器中:(其实不需要配置:只有启动类扫描不到的且需要被注册为bean的类才需要配置,如@Configuration等注解标注的类)
-
cas调试debug
build.cmd debug方式启动。
并且配置一个启动项,在build.cmd debug命令执行完成后启动
7.cas客户端一个项目启动多个端口
二、第二个问题
自定义异常在登录页面生效,但是提示信息是国际化文件配置的固定信息,不是代码抛出异常时括号中定义的信息。
throw new MyFailedLoginException("account and password do not match!");
-
继续改造目录标题一5.2中已建好的AuthenticationExceptionHandlerAction.java文件中的handleAuthenticationException方法
//获取自定义异常携带的自定义提示信息并通过.defaultText(customerExceptionMsg)向下传递
package org.apereo.cas.web.flow.actions;
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import com.genertech.plm.aia.login.cas.customerAuthentication.MyAccountDisabledException;
import com.genertech.plm.aia.login.cas.customerAuthentication.MyAccountLockedException;
import com.genertech.plm.aia.login.cas.customerAuthentication.MyFailedLoginException;
import lombok.Generated;
import org.apereo.cas.authentication.AuthenticationException;
import org.apereo.cas.services.UnauthorizedServiceForPrincipalException;
import org.apereo.cas.ticket.AbstractTicketException;
import org.apereo.cas.web.support.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.binding.message.MessageBuilder;
import org.springframework.binding.message.MessageContext;
import org.springframework.webflow.action.AbstractAction;
import org.springframework.webflow.action.EventFactorySupport;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;
import java.net.URI;
import java.util.*;
import java.util.stream.Collectors;
/**
* 该类规定了自定义异常类应以My开头
* 覆盖修改AuthenticationExceptionHandlerAction.java源码,解决自定义异常登录界面提示信息无效 和
* 自定义异常的msg在登录界面不展示提示信息(展示的是国际化配置文件配置的固定信息)
*/
public class AuthenticationExceptionHandlerAction extends AbstractAction {
@Generated
private static final Logger LOGGER = LoggerFactory.getLogger(AuthenticationExceptionHandlerAction.class);
private static final String DEFAULT_MESSAGE_BUNDLE_PREFIX = "authenticationFailure.";
private static final String UNKNOWN = "UNKNOWN";
private final Set<Class<? extends Throwable>> errors;
private String messageBundlePrefix;
public AuthenticationExceptionHandlerAction() {
this(new LinkedHashSet());
}
public AuthenticationExceptionHandlerAction(final Set<Class<? extends Throwable>> errors) {
this.messageBundlePrefix = "authenticationFailure.";
this.errors = errors;
}
public Set<Class<? extends Throwable>> getErrors() {
return new LinkedHashSet(this.errors);
}
public String handle(final Exception e, final RequestContext requestContext) {
MessageContext messageContext = requestContext.getMessageContext();
if (e instanceof AuthenticationException) {
return this.handleAuthenticationException((AuthenticationException) e, requestContext);
} else if (e instanceof AbstractTicketException) {
return this.handleAbstractTicketException((AbstractTicketException) e, requestContext);
} else {
LOGGER.trace("Unable to translate errors of the authentication exception [{}]. Returning [{}]", e, "UNKNOWN");
String messageCode = this.messageBundlePrefix + "UNKNOWN";
messageContext.addMessage((new MessageBuilder()).error().code(messageCode).build());
return "UNKNOWN";
}
}
protected String handleAuthenticationException(final AuthenticationException e, final RequestContext requestContext) {
if (e.getHandlerErrors().containsKey(UnauthorizedServiceForPrincipalException.class.getSimpleName())) {
URI url = WebUtils.getUnauthorizedRedirectUrlIntoFlowScope(requestContext);
if (url != null) {
LOGGER.warn("Unauthorized service access for principal; CAS will be redirecting to [{}]", url);
return "serviceUnauthorizedCheck";
}
}
Collection<Class> values = (Collection) e.getHandlerErrors().values().stream().map(Object::getClass).collect(Collectors.toList());
Objects.requireNonNull(values);
ArrayList<Class> tempList = new ArrayList(values);
Class myException = null;
for (Class clazz : tempList) {
// 自定义登录异常类,必须以My开头
if (clazz.getSimpleName().contains("My")) {
myException = clazz;
break;
}
}
if (myException == null) {
myException = tempList.get(0);
}
String handlerErrorName = myException.getSimpleName();
MessageContext messageContext = requestContext.getMessageContext();
String messageCode = this.messageBundlePrefix + handlerErrorName;
// 获取自定义异常携带的自定义提示信息并通过.defaultText(customerExceptionMsg)向下传递
// 这里只处理自定义抛出的异常
String customerExceptionMsg = "认证信息无效。";
boolean b = false;
if (handlerErrorName.equals("MyFailedLoginException")) {
List<Throwable> list = e.getHandlerErrors().values().stream()
.filter(O -> O.getClass().getSimpleName().equals("MyFailedLoginException"))
.collect(Collectors.toList());
MyFailedLoginException myFailedLoginException = (MyFailedLoginException) list.get(0);
customerExceptionMsg = myFailedLoginException.getMessage();
} else if (handlerErrorName.equals("MyAccountDisabledException")) {
List<Throwable> list = e.getHandlerErrors().values().stream()
.filter(O -> O.getClass().getSimpleName().equals("MyAccountDisabledException"))
.collect(Collectors.toList());
MyAccountDisabledException myAccountDisabledException = (MyAccountDisabledException) list.get(0);
customerExceptionMsg = myAccountDisabledException.getMessage();
} else if (handlerErrorName.equals("MyAccountLockedException")) {
List<Throwable> list = e.getHandlerErrors().values().stream()
.filter(O -> O.getClass().getSimpleName().equals("MyAccountLockedException"))
.collect(Collectors.toList());
MyAccountLockedException myAccountLockedException = (MyAccountLockedException) list.get(0);
customerExceptionMsg = myAccountLockedException.getMessage();
}
// 如果抛出自定义登录异常时未配置message,getMessage()时customerExceptionMsg="No supported authentication handlers found for given credentials"
// 这里置为空字符串,才能在界面展示国际化文件配置的固定提示信息
if ("No supported authentication handlers found for given credentials".equals(customerExceptionMsg)) {
customerExceptionMsg = "";
}
messageContext.addMessage((new MessageBuilder()).error().defaultText(customerExceptionMsg).code(messageCode).build());
return handlerErrorName;
}
protected String handleAbstractTicketException(final AbstractTicketException e, final RequestContext requestContext) {
MessageContext messageContext = requestContext.getMessageContext();
Optional<String> match = this.errors.stream().filter((c) -> {
return c.isInstance(e);
}).map(Class::getSimpleName).findFirst();
match.ifPresent((s) -> {
messageContext.addMessage((new MessageBuilder()).error().code(e.getCode()).build());
});
return (String) match.orElse("UNKNOWN");
}
protected Event doExecute(final RequestContext requestContext) {
Event currentEvent = requestContext.getCurrentEvent();
LOGGER.debug("Located current event [{}]", currentEvent);
Exception error = (Exception) currentEvent.getAttributes().get("error", Exception.class);
if (error != null) {
LOGGER.debug("Located error attribute [{}] with message [{}] from the current event", error.getClass(), error.getMessage());
String event = this.handle(error, requestContext);
LOGGER.debug("Final event id resolved from the error is [{}]", event);
return (new EventFactorySupport()).event(this, event, currentEvent.getAttributes());
} else {
return (new EventFactorySupport()).event(this, "error");
}
}
}
2.复制改造粘贴源码DefaultMessageResolver.java
DefaultMessageResolver.java改造结果(仅修改了resolveMessage方法):
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.binding.message;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceResolvable;
import org.springframework.core.style.ToStringCreator;
import java.util.Locale;
/**
* 覆盖修改DefaultMessageResolver.java源码,解决自定义异常的msg在登录界面不展示提示信息(展示的是国际化配置文件配置的固定信息)
*/
public class DefaultMessageResolver implements MessageResolver, MessageSourceResolvable {
private Object source;
private String[] codes;
private Severity severity;
private Object[] args;
private String defaultText;
public DefaultMessageResolver(Object source, String[] codes, Severity severity, Object[] args, String defaultText) {
this.source = source;
this.codes = codes;
this.severity = severity;
this.args = args;
this.defaultText = defaultText;
}
public Message resolveMessage(MessageSource messageSource, Locale locale) {
if(StringUtils.isEmpty(this.defaultText)){
// 从国家化文件取提示信息
return new Message(this.source, this.postProcessMessageText(messageSource.getMessage(this, locale)), this.severity);
} else {
// 使用自己配置的msg作为提示信息
return new Message(this.source, this.defaultText, this.severity);
}
}
protected String postProcessMessageText(String text) {
return text;
}
public String[] getCodes() {
return this.codes;
}
public Object[] getArguments() {
return this.args;
}
public String getDefaultMessage() {
return this.defaultText;
}
public String toString() {
return (new ToStringCreator(this)).append("source", this.source).append("severity", this.severity).append("codes", this.codes).append("args", this.args).append("defaultText", this.defaultText).toString();
}
}