mirror of
https://gitee.com/dromara/MaxKey.git
synced 2026-05-14 20:50:14 +08:00
Authentication Forward Fix
Authentication Forward Fix
This commit is contained in:
@@ -87,6 +87,10 @@ public abstract class AbstractAuthenticationProvider {
|
||||
.getAttribute(WebConstants.CURRENT_LOGIN_USER_PASSWORD_SET_TYPE);
|
||||
// 登录完成后切换SESSION
|
||||
_logger.debug("Login Session {}.", WebContext.getSession().getId());
|
||||
|
||||
final Object firstSavedRequest =
|
||||
WebContext.getAttribute(WebConstants.FIRST_SAVED_REQUEST_PARAMETER);
|
||||
|
||||
WebContext.getSession().invalidate();
|
||||
WebContext.setAttribute(
|
||||
WebConstants.CURRENT_USER_SESSION_ID, WebContext.getSession().getId());
|
||||
@@ -95,6 +99,7 @@ public abstract class AbstractAuthenticationProvider {
|
||||
authenticationRealm.insertLoginHistory(
|
||||
userInfo, ConstantsLoginType.LOCAL, "", "xe00000004", "success");
|
||||
|
||||
WebContext.setAttribute(WebConstants.FIRST_SAVED_REQUEST_PARAMETER,firstSavedRequest);
|
||||
// 认证设置
|
||||
WebContext.setAuthentication(authentication);
|
||||
WebContext.setUserInfo(userInfo);
|
||||
|
||||
@@ -20,45 +20,47 @@ import org.springframework.security.web.savedrequest.SavedRequest;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* An authentication success strategy which can make use of the {@link DefaultSavedRequest} which may have been stored in
|
||||
* the session by the {@link ExceptionTranslationFilter}. When such a request is intercepted and requires authentication,
|
||||
* the request data is stored to record the original destination before the authentication process commenced, and to
|
||||
* allow the request to be reconstructed when a redirect to the same URL occurs. This class is responsible for
|
||||
* performing the redirect to the original URL if appropriate.
|
||||
* An authentication success strategy which can make use of the
|
||||
* {@link DefaultSavedRequest} which may have been stored in the session by the
|
||||
* {@link ExceptionTranslationFilter}. When such a request is intercepted and
|
||||
* requires authentication, the request data is stored to record the original
|
||||
* destination before the authentication process commenced, and to allow the
|
||||
* request to be reconstructed when a redirect to the same URL occurs. This
|
||||
* class is responsible for performing the redirect to the original URL if
|
||||
* appropriate.
|
||||
* <p>
|
||||
* Following a successful authentication, it decides on the redirect destination, based on the following scenarios:
|
||||
* Following a successful authentication, it decides on the redirect
|
||||
* destination, based on the following scenarios:
|
||||
* <ul>
|
||||
* <li>
|
||||
* If the {@code alwaysUseDefaultTargetUrl} property is set to true, the {@code defaultTargetUrl}
|
||||
* will be used for the destination. Any {@code DefaultSavedRequest} stored in the session will be
|
||||
* removed.
|
||||
* </li>
|
||||
* <li>
|
||||
* If the {@code targetUrlParameter} has been set on the request, the value will be used as the destination.
|
||||
* Any {@code DefaultSavedRequest} will again be removed.
|
||||
* </li>
|
||||
* <li>
|
||||
* If a {@link SavedRequest} is found in the {@code RequestCache} (as set by the {@link ExceptionTranslationFilter} to
|
||||
* record the original destination before the authentication process commenced), a redirect will be performed to the
|
||||
* Url of that original destination. The {@code SavedRequest} object will remain cached and be picked up
|
||||
* when the redirected request is received
|
||||
* (See {@link org.springframework.security.web.savedrequest.SavedRequestAwareWrapper SavedRequestAwareWrapper}).
|
||||
* </li>
|
||||
* <li>
|
||||
* If no {@code SavedRequest} is found, it will delegate to the base class.
|
||||
* <li>If the {@code alwaysUseDefaultTargetUrl} property is set to true, the
|
||||
* {@code defaultTargetUrl} will be used for the destination. Any
|
||||
* {@code DefaultSavedRequest} stored in the session will be removed.</li>
|
||||
* <li>If the {@code targetUrlParameter} has been set on the request, the value
|
||||
* will be used as the destination. Any {@code DefaultSavedRequest} will again
|
||||
* be removed.</li>
|
||||
* <li>If a {@link SavedRequest} is found in the {@code RequestCache} (as set by
|
||||
* the {@link ExceptionTranslationFilter} to record the original destination
|
||||
* before the authentication process commenced), a redirect will be performed to
|
||||
* the Url of that original destination. The {@code SavedRequest} object will
|
||||
* remain cached and be picked up when the redirected request is received (See
|
||||
* {@link org.springframework.security.web.savedrequest.SavedRequestAwareWrapper
|
||||
* SavedRequestAwareWrapper}).</li>
|
||||
* <li>If no {@code SavedRequest} is found, it will delegate to the base class.
|
||||
* </li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Luke Taylor
|
||||
* @since 3.0
|
||||
*/
|
||||
public class SavedRequestAwareAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
|
||||
protected final Logger _logger = LoggerFactory.getLogger(SavedRequestAwareAuthenticationSuccessHandler.class);
|
||||
public class SavedRequestAwareAuthenticationSuccessHandler
|
||||
extends SimpleUrlAuthenticationSuccessHandler {
|
||||
protected final Logger _logger = LoggerFactory.getLogger(
|
||||
SavedRequestAwareAuthenticationSuccessHandler.class);
|
||||
|
||||
@Autowired
|
||||
@Qualifier("remeberMeService")
|
||||
protected AbstractRemeberMeService remeberMeService;
|
||||
|
||||
@Qualifier("remeberMeService")
|
||||
protected AbstractRemeberMeService remeberMeService;
|
||||
|
||||
private RequestCache requestCache = new HttpSessionRequestCache();
|
||||
|
||||
@Override
|
||||
@@ -66,15 +68,18 @@ public class SavedRequestAwareAuthenticationSuccessHandler extends SimpleUrlAuth
|
||||
Authentication authentication) throws ServletException, IOException {
|
||||
SavedRequest savedRequest = requestCache.getRequest(request, response);
|
||||
|
||||
remeberMeService.createRemeberMe(authentication.getPrincipal().toString(), request, response);
|
||||
|
||||
remeberMeService.createRemeberMe(
|
||||
authentication.getPrincipal().toString(), request, response);
|
||||
|
||||
if (savedRequest == null) {
|
||||
super.onAuthenticationSuccess(request, response, authentication);
|
||||
|
||||
return;
|
||||
}
|
||||
String targetUrlParameter = getTargetUrlParameter();
|
||||
if (isAlwaysUseDefaultTargetUrl() || (targetUrlParameter != null && StringUtils.hasText(request.getParameter(targetUrlParameter)))) {
|
||||
if (isAlwaysUseDefaultTargetUrl()
|
||||
|| (targetUrlParameter != null
|
||||
&& StringUtils.hasText(request.getParameter(targetUrlParameter)))) {
|
||||
requestCache.removeRequest(request, response);
|
||||
super.onAuthenticationSuccess(request, response, authentication);
|
||||
|
||||
@@ -82,16 +87,18 @@ public class SavedRequestAwareAuthenticationSuccessHandler extends SimpleUrlAuth
|
||||
}
|
||||
|
||||
clearAuthenticationAttributes(request);
|
||||
|
||||
|
||||
// Use the DefaultSavedRequest URL
|
||||
String targetUrl = savedRequest.getRedirectUrl();
|
||||
|
||||
//is cas login , with service parameter
|
||||
logger.info( request.getParameter(WebConstants.CAS_SERVICE_PARAMETER));
|
||||
if(request.getParameter(WebConstants.CAS_SERVICE_PARAMETER)!=null && request.getParameter(WebConstants.CAS_SERVICE_PARAMETER).startsWith("http")){
|
||||
targetUrl=WebContext.getHttpContextPath()+"/authorize/cas/login?service="+request.getParameter(WebConstants.CAS_SERVICE_PARAMETER);
|
||||
|
||||
// is cas login , with service parameter
|
||||
logger.info("CAS " + request.getParameter(WebConstants.CAS_SERVICE_PARAMETER));
|
||||
if (request.getParameter(WebConstants.CAS_SERVICE_PARAMETER) != null
|
||||
&& request.getParameter(WebConstants.CAS_SERVICE_PARAMETER).startsWith("http")) {
|
||||
targetUrl = WebContext.getHttpContextPath() + "/authorize/cas/login?service="
|
||||
+ request.getParameter(WebConstants.CAS_SERVICE_PARAMETER);
|
||||
}
|
||||
|
||||
targetUrl = targetUrl == null ? "/forwardindex" : targetUrl;
|
||||
logger.debug("Redirecting to DefaultSavedRequest Url: " + targetUrl);
|
||||
getRedirectStrategy().sendRedirect(request, response, targetUrl);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package org.maxkey.authn.realm;
|
||||
|
||||
/**
|
||||
* IAuthenticationServer .
|
||||
* @author Crystal.Sea
|
||||
*
|
||||
*/
|
||||
public interface IAuthenticationServer {
|
||||
|
||||
public boolean authenticate(String username, String password) ;
|
||||
public boolean authenticate(String username, String password);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package org.maxkey.web;
|
||||
|
||||
/**
|
||||
* Web Application Constants define
|
||||
* Web Application Constants define.
|
||||
*
|
||||
* @author Crystal.Sea
|
||||
*
|
||||
@@ -32,9 +32,7 @@ public class WebConstants {
|
||||
public static final String CURRENT_MESSAGE = "current_message";
|
||||
|
||||
// SPRING_SECURITY_SAVED_REQUEST
|
||||
public static final String SPRING_PROCESS_SAVED_REQUEST = "SPRING_SECURITY_SAVED_REQUEST";
|
||||
|
||||
public static final String FIRST_SAVED_REQUEST_PARAMETER = "first_saved_request_parameter";
|
||||
public static final String FIRST_SAVED_REQUEST_PARAMETER = "SPRING_SECURITY_SAVED_REQUEST";
|
||||
|
||||
public static final String KAPTCHA_SESSION_KEY = "kaptcha_session_key";
|
||||
|
||||
|
||||
@@ -71,7 +71,6 @@ import org.springframework.util.StringUtils;
|
||||
* @see org.apache.velocity.app.VelocityEngine
|
||||
* @deprecated as of Spring 4.3, in favor of FreeMarker
|
||||
*/
|
||||
@Deprecated
|
||||
public class VelocityEngineFactory {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
@@ -48,7 +48,6 @@ import org.springframework.context.ResourceLoaderAware;
|
||||
* @see org.springframework.web.servlet.view.velocity.VelocityConfigurer
|
||||
* @deprecated as of Spring 4.3, in favor of FreeMarker
|
||||
*/
|
||||
@Deprecated
|
||||
public class VelocityEngineFactoryBean extends VelocityEngineFactory
|
||||
implements FactoryBean<VelocityEngine>, InitializingBean, ResourceLoaderAware {
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@ import org.apache.velocity.exception.VelocityException;
|
||||
* @since 22.01.2004
|
||||
* @deprecated as of Spring 4.3, in favor of FreeMarker
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class VelocityEngineUtils {
|
||||
|
||||
/**
|
||||
@@ -46,7 +45,6 @@ public abstract class VelocityEngineUtils {
|
||||
* @deprecated Use {@link #mergeTemplate(VelocityEngine, String, String, Map, Writer)}
|
||||
* instead, following Velocity 1.6's corresponding deprecation in its own API.
|
||||
*/
|
||||
@Deprecated
|
||||
public static void mergeTemplate(
|
||||
VelocityEngine velocityEngine, String templateLocation, Map<String, Object> model, Writer writer)
|
||||
throws VelocityException {
|
||||
@@ -86,7 +84,6 @@ public abstract class VelocityEngineUtils {
|
||||
* @deprecated Use {@link #mergeTemplateIntoString(VelocityEngine, String, String, Map)}
|
||||
* instead, following Velocity 1.6's corresponding deprecation in its own API.
|
||||
*/
|
||||
@Deprecated
|
||||
public static String mergeTemplateIntoString(VelocityEngine velocityEngine, String templateLocation,
|
||||
Map<String, Object> model) throws VelocityException {
|
||||
|
||||
|
||||
@@ -43,16 +43,21 @@ public class MaxKeyMgtMvcConfig implements WebMvcConfigurer {
|
||||
.addPathPatterns("/orgs/**")
|
||||
.addPathPatterns("/userinfo/**")
|
||||
.addPathPatterns("/apps/**")
|
||||
.addPathPatterns("/app/accounts/**")
|
||||
.addPathPatterns("/groups/**")
|
||||
.addPathPatterns("/groupMember/**")
|
||||
.addPathPatterns("/groupPrivileges/**")
|
||||
.addPathPatterns("/roles/**")
|
||||
.addPathPatterns("/rolemembers/**")
|
||||
.addPathPatterns("/resources/**")
|
||||
.addPathPatterns("/permissions/**")
|
||||
.addPathPatterns("/config/**")
|
||||
.addPathPatterns("/logs/**")
|
||||
;
|
||||
|
||||
_logger.debug("add PermissionAdapter");
|
||||
|
||||
registry.addInterceptor(historyLogsAdapter)
|
||||
.addPathPatterns("/users/*")
|
||||
.addPathPatterns("/userinfo/**")
|
||||
.addPathPatterns("/enterprises/**")
|
||||
.addPathPatterns("/employees/**")
|
||||
@@ -64,6 +69,7 @@ public class MaxKeyMgtMvcConfig implements WebMvcConfigurer {
|
||||
.addPathPatterns("/approles/**")
|
||||
;
|
||||
_logger.debug("add HistoryLogsAdapter");
|
||||
|
||||
registry.addInterceptor(localeChangeInterceptor);
|
||||
_logger.debug("add LocaleChangeInterceptor");
|
||||
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package org.maxkey.web.endpoint;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import org.maxkey.config.ApplicationConfig;
|
||||
import org.maxkey.constants.ConstantsPasswordSetType;
|
||||
import org.maxkey.domain.UserInfo;
|
||||
@@ -25,9 +30,10 @@ public class IndexEndpoint {
|
||||
@Autowired
|
||||
@Qualifier("applicationConfig")
|
||||
ApplicationConfig applicationConfig;
|
||||
|
||||
@RequestMapping(value={"/forwardindex"})
|
||||
public ModelAndView forwardindex() {
|
||||
public ModelAndView forwardindex(HttpServletRequest request,
|
||||
HttpServletResponse response) throws ServletException, IOException {
|
||||
|
||||
_logger.debug("IndexEndpoint /forwardindex.");
|
||||
ModelAndView modelAndView=new ModelAndView();
|
||||
Integer passwordSetType=(Integer)WebContext.getSession().getAttribute(WebConstants.CURRENT_LOGIN_USER_PASSWORD_SET_TYPE);
|
||||
@@ -54,11 +60,14 @@ public class IndexEndpoint {
|
||||
return modelAndView;
|
||||
}
|
||||
|
||||
|
||||
|
||||
return new ModelAndView("index");
|
||||
}
|
||||
|
||||
@RequestMapping(value={"/index"})
|
||||
public ModelAndView home() {
|
||||
public ModelAndView home(HttpServletRequest request,
|
||||
HttpServletResponse response) throws ServletException, IOException {
|
||||
_logger.debug("IndexEndpoint /index.");
|
||||
|
||||
if(applicationConfig.getLoginConfig().getDefaultUri()!=null&&
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package org.maxkey.web.endpoint;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
@@ -22,9 +24,6 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
|
||||
import org.springframework.security.web.savedrequest.RequestCache;
|
||||
import org.springframework.security.web.savedrequest.SavedRequest;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.web.bind.annotation.CookieValue;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
@@ -148,18 +147,7 @@ public class LoginEndpoint {
|
||||
modelAndView.addObject("ssopList", socialSignOnProviderService.getSocialSignOnProviders());
|
||||
}
|
||||
}
|
||||
//save first protected url
|
||||
SavedRequest firstSavedRequest = (SavedRequest)WebContext.getAttribute(WebConstants.FIRST_SAVED_REQUEST_PARAMETER);
|
||||
if(firstSavedRequest==null){
|
||||
RequestCache requestCache = new HttpSessionRequestCache();
|
||||
SavedRequest savedRequest =requestCache.getRequest(request, response);
|
||||
if(savedRequest!=null){
|
||||
_logger.debug("first request parameter "+savedRequest.getRedirectUrl());
|
||||
WebContext.setAttribute(WebConstants.FIRST_SAVED_REQUEST_PARAMETER, savedRequest);
|
||||
}
|
||||
}else {
|
||||
WebContext.setAttribute(WebConstants.SPRING_PROCESS_SAVED_REQUEST, firstSavedRequest);
|
||||
}
|
||||
|
||||
|
||||
if(isAuthenticated){
|
||||
return WebContext.redirect("/forwardindex");
|
||||
@@ -169,12 +157,15 @@ public class LoginEndpoint {
|
||||
}
|
||||
|
||||
@RequestMapping(value={"/logon.do"})
|
||||
public ModelAndView logon(@ModelAttribute("authentication") BasicAuthentication authentication) {
|
||||
public ModelAndView logon(
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
@ModelAttribute("authentication") BasicAuthentication authentication) throws ServletException, IOException {
|
||||
|
||||
authenticationProvider.authenticate(authentication);
|
||||
|
||||
if(WebContext.isAuthenticated()){
|
||||
return WebContext.redirect("/forwardindex");
|
||||
return WebContext.redirect("/forwardindex");
|
||||
}else{
|
||||
return WebContext.redirect("/login");
|
||||
}
|
||||
|
||||
@@ -4,12 +4,18 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.maxkey.authn.SavedRequestAwareAuthenticationSuccessHandler;
|
||||
import org.maxkey.config.ApplicationConfig;
|
||||
import org.maxkey.web.WebConstants;
|
||||
import org.maxkey.web.WebContext;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.security.web.savedrequest.HttpSessionRequestCache;
|
||||
import org.springframework.security.web.savedrequest.RequestCache;
|
||||
import org.springframework.security.web.savedrequest.SavedRequest;
|
||||
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
|
||||
|
||||
/**
|
||||
@@ -26,6 +32,11 @@ public class PermissionAdapter extends HandlerInterceptorAdapter {
|
||||
@Qualifier("applicationConfig")
|
||||
private ApplicationConfig applicationConfig;
|
||||
|
||||
|
||||
@Autowired
|
||||
@Qualifier("savedRequestSuccessHandler")
|
||||
SavedRequestAwareAuthenticationSuccessHandler savedRequestSuccessHandler;
|
||||
|
||||
static ConcurrentHashMap<String, String> navigationsMap = null;
|
||||
|
||||
/*
|
||||
@@ -41,14 +52,34 @@ public class PermissionAdapter extends HandlerInterceptorAdapter {
|
||||
HttpServletResponse response, Object handler)
|
||||
throws Exception {
|
||||
_logger.trace("PermissionAdapter preHandle");
|
||||
//save first protected url
|
||||
SavedRequest firstSavedRequest = (SavedRequest)WebContext.getAttribute(WebConstants.FIRST_SAVED_REQUEST_PARAMETER);
|
||||
// 判断用户是否登录, 判断用户和角色,判断用户是否登录用户
|
||||
if (WebContext.getAuthentication() == null
|
||||
|| WebContext.getAuthentication().getAuthorities() == null) {
|
||||
//保存未认证的请求信息
|
||||
if(firstSavedRequest==null){
|
||||
RequestCache requestCache = new HttpSessionRequestCache();
|
||||
requestCache.saveRequest(request, response);
|
||||
SavedRequest savedRequest =requestCache.getRequest(request, response);
|
||||
if(savedRequest!=null){
|
||||
_logger.debug("first request parameter savedRequest "+savedRequest.getRedirectUrl());
|
||||
WebContext.setAttribute(WebConstants.FIRST_SAVED_REQUEST_PARAMETER, savedRequest);
|
||||
savedRequestSuccessHandler.setRequestCache(requestCache);
|
||||
}
|
||||
}
|
||||
|
||||
_logger.trace("No Authentication ... forward to /login");
|
||||
RequestDispatcher dispatcher = request.getRequestDispatcher("/login");
|
||||
dispatcher.forward(request, response);
|
||||
return false;
|
||||
}
|
||||
|
||||
//认证完成,跳转到未认证请求
|
||||
if(firstSavedRequest!=null) {
|
||||
savedRequestSuccessHandler.onAuthenticationSuccess(request, response, WebContext.getAuthentication());
|
||||
WebContext.removeAttribute(WebConstants.FIRST_SAVED_REQUEST_PARAMETER);
|
||||
}
|
||||
|
||||
boolean hasAccess = true;
|
||||
|
||||
|
||||
@@ -185,6 +185,18 @@
|
||||
<mvc:mapping path="/historys/**" />
|
||||
<mvc:mapping path="/appList/**" />
|
||||
<mvc:mapping path="/socialsignon/**" />
|
||||
|
||||
<mvc:mapping path="/authz/basic/*" />
|
||||
<mvc:mapping path="/authz/ltpa/*" />
|
||||
<mvc:mapping path="/authz/desktop/*" />
|
||||
<mvc:mapping path="/authz/formbased/*" />
|
||||
<mvc:mapping path="/authz/tokenbased/*"/>
|
||||
<mvc:mapping path="/authz/saml20/idpinit/*"/>
|
||||
<mvc:mapping path="/authz/saml20/assertion"/>
|
||||
<mvc:mapping path="/authz/cas/login"/>
|
||||
<mvc:mapping path="/authz/cas/granting"/>
|
||||
<mvc:mapping path="/oauth/v20/authorize"/>
|
||||
|
||||
<bean class="org.maxkey.web.interceptor.PermissionAdapter" />
|
||||
</mvc:interceptor>
|
||||
|
||||
|
||||
@@ -3,9 +3,11 @@
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=UTF-8" />
|
||||
<title>MaxKey</title>
|
||||
<base href="<@base />"/>
|
||||
<link rel="shortcut icon" type="image/x-icon" href="<@base />/static/images/favicon.ico"/>
|
||||
<link type="text/css" rel="stylesheet" href="<@base url="/style.css"/>" />
|
||||
</head>
|
||||
<body>
|
||||
<script type="text/javascript">window.top.location.href="<@basePath />/forwardindex";
|
||||
<script type="text/javascript">window.top.location.href="<@basePath />/forwardindex";</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user