一、
s:authorize定义:
<%@ taglib uri="http://www.springframework.org/security/tags" prefix="s"%>为security自定义标签
环境版本:security4.1.3
二、问题描述
标签使用:
<s:authorize access="hasAnyRole('USER','ADMIN')">
<li><a href="<%=basePathHeader %>logout">退出 Logout </a></li>
<li><a href="<%=basePathHeader %>xxxx" class="current">修改密码 Change Password</a></li>
</s:authorize>
从没有访问权限页面forward方式跳转过来该标签不起作用,(redirect方式没问题)
三、问题定位
初步定位为跳转方式不同引起,看源码:
1.跳转的时候授权信息Authentication会被置空
SecurityContextImpl.setAuthentication(Authentication) line: 72
ExceptionTranslationFilter.sendStartAuthentication(HttpServletRequest, HttpServletResponse, FilterChain, AuthenticationException) line: 201
ExceptionTranslationFilter.handleSpringSecurityException(HttpServletRequest, HttpServletResponse, FilterChain, RuntimeException) line: 178
ExceptionTranslationFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 134
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331
SessionManagementFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 137
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331
AnonymousAuthenticationFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 111
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331
SecurityContextHolderAwareRequestFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 169
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331
RequestCacheAwareFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 63
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331
BasicAuthenticationFilter.doFilterInternal(HttpServletRequest, HttpServletResponse, FilterChain) line: 158
BasicAuthenticationFilter(OncePerRequestFilter).doFilter(ServletRequest, ServletResponse, FilterChain) line: 107
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331
UsernamePasswordAuthenticationFilter(AbstractAuthenticationProcessingFilter).doFilter(ServletRequest, ServletResponse, FilterChain) line: 200
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331
LogoutFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 121
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331
HeaderWriterFilter.doFilterInternal(HttpServletRequest, HttpServletResponse, FilterChain) line: 66
HeaderWriterFilter(OncePerRequestFilter).doFilter(ServletRequest, ServletResponse, FilterChain) line: 107
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331
WebAsyncManagerIntegrationFilter.doFilterInternal(HttpServletRequest, HttpServletResponse, FilterChain) line: 56
WebAsyncManagerIntegrationFilter(OncePerRequestFilter).doFilter(ServletRequest, ServletResponse, FilterChain) line: 107
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331
ConcurrentSessionFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 134
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331
SecurityContextPersistenceFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 105
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331
FilterChainProxy.doFilterInternal(ServletRequest, ServletResponse, FilterChain) line: 214
FilterChainProxy.doFilter(ServletRequest, ServletResponse, FilterChain) line: 177
DelegatingFilterProxy.invokeDelegate(Filter, ServletRequest, ServletResponse, FilterChain) line: 346
DelegatingFilterProxy.doFilter(ServletRequest, ServletResponse, FilterChain) line: 262
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 243
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 210
SiteMeshFilter(ContentBufferingFilter).bufferAndPostProcess(FilterChain, HttpServletRequest, HttpServletResponse, Selector) line: 169
SiteMeshFilter(ContentBufferingFilter).doFilter(ServletRequest, ServletResponse, FilterChain) line: 126
SiteMeshFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 120
ConfigurableSiteMeshFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 163
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 243
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 210
CharacterEncodingFilter.doFilterInternal(HttpServletRequest, HttpServletResponse, FilterChain) line: 197
CharacterEncodingFilter(OncePerRequestFilter).doFilter(ServletRequest, ServletResponse, FilterChain) line: 107
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 243
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 210
StandardWrapperValve.invoke(Request, Response) line: 222
StandardContextValve.invoke(Request, Response) line: 123
NonLoginAuthenticator(AuthenticatorBase).invoke(Request, Response) line: 472
StandardHostValve.invoke(Request, Response) line: 168
ErrorReportValve.invoke(Request, Response) line: 99
AccessLogValve.invoke(Request, Response) line: 929
StandardEngineValve.invoke(Request, Response) line: 118
CoyoteAdapter.service(Request, Response) line: 407
Http11AprProcessor(AbstractHttp11Processor<S>).process(SocketWrapper<S>) line: 1002
Http11AprProtocol$Http11ConnectionHandler(AbstractProtocol$AbstractConnectionHandler<S,P>).process(SocketWrapper<S>, SocketStatus) line: 585
AprEndpoint$SocketProcessor.run() line: 1813
ThreadPoolExecutor(ThreadPoolExecutor).runWorker(ThreadPoolExecutor$Worker) line: 1145
ThreadPoolExecutor$Worker.run() line: 615
TaskThread(Thread).run() line: 745
ExceptionTranslationFilter.java
protected void sendStartAuthentication(HttpServletRequest request,
HttpServletResponse response, FilterChain chain,
AuthenticationException reason) throws ServletException, IOException {
// SEC-112: Clear the SecurityContextHolder's Authentication, as the
// existing Authentication is no longer considered valid
SecurityContextHolder.getContext().setAuthentication(null); //清空
requestCache.saveRequest(request, response);
logger.debug("Calling Authentication entry point.");
authenticationEntryPoint.commence(request, response, reason);
}
2. redirect方式会走过滤器AnonymousAuthenticationFilter,会创建Authentication,授权信息
SecurityContextImpl.setAuthentication(Authentication) line: 72
AnonymousAuthenticationFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 96
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331
SecurityContextHolderAwareRequestFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 169
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331
RequestCacheAwareFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 63
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331
BasicAuthenticationFilter.doFilterInternal(HttpServletRequest, HttpServletResponse, FilterChain) line: 158
BasicAuthenticationFilter(OncePerRequestFilter).doFilter(ServletRequest, ServletResponse, FilterChain) line: 107
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331
UsernamePasswordAuthenticationFilter(AbstractAuthenticationProcessingFilter).doFilter(ServletRequest, ServletResponse, FilterChain) line: 200
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331
LogoutFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 121
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331
HeaderWriterFilter.doFilterInternal(HttpServletRequest, HttpServletResponse, FilterChain) line: 66
HeaderWriterFilter(OncePerRequestFilter).doFilter(ServletRequest, ServletResponse, FilterChain) line: 107
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331
WebAsyncManagerIntegrationFilter.doFilterInternal(HttpServletRequest, HttpServletResponse, FilterChain) line: 56
WebAsyncManagerIntegrationFilter(OncePerRequestFilter).doFilter(ServletRequest, ServletResponse, FilterChain) line: 107
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331
ConcurrentSessionFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 134
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331
SecurityContextPersistenceFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 105
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 331
FilterChainProxy.doFilterInternal(ServletRequest, ServletResponse, FilterChain) line: 214
FilterChainProxy.doFilter(ServletRequest, ServletResponse, FilterChain) line: 177
DelegatingFilterProxy.invokeDelegate(Filter, ServletRequest, ServletResponse, FilterChain) line: 346
DelegatingFilterProxy.doFilter(ServletRequest, ServletResponse, FilterChain) line: 262
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 243
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 210
SiteMeshFilter(ContentBufferingFilter).bufferAndPostProcess(FilterChain, HttpServletRequest, HttpServletResponse, Selector) line: 169
SiteMeshFilter(ContentBufferingFilter).doFilter(ServletRequest, ServletResponse, FilterChain) line: 126
SiteMeshFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 120
ConfigurableSiteMeshFilter.doFilter(ServletRequest, ServletResponse, FilterChain) line: 163
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 243
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 210
CharacterEncodingFilter.doFilterInternal(HttpServletRequest, HttpServletResponse, FilterChain) line: 197
CharacterEncodingFilter(OncePerRequestFilter).doFilter(ServletRequest, ServletResponse, FilterChain) line: 107
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 243
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 210
StandardWrapperValve.invoke(Request, Response) line: 222
StandardContextValve.invoke(Request, Response) line: 123
NonLoginAuthenticator(AuthenticatorBase).invoke(Request, Response) line: 472
StandardHostValve.invoke(Request, Response) line: 168
ErrorReportValve.invoke(Request, Response) line: 99
AccessLogValve.invoke(Request, Response) line: 929
StandardEngineValve.invoke(Request, Response) line: 118
CoyoteAdapter.service(Request, Response) line: 407
Http11AprProcessor(AbstractHttp11Processor<S>).process(SocketWrapper<S>) line: 1002
Http11AprProtocol$Http11ConnectionHandler(AbstractProtocol$AbstractConnectionHandler<S,P>).process(SocketWrapper<S>, SocketStatus) line: 585
AprEndpoint$SocketProcessor.run() line: 1813
ThreadPoolExecutor(ThreadPoolExecutor).runWorker(ThreadPoolExecutor$Worker) line: 1145
ThreadPoolExecutor$Worker.run() line: 615
TaskThread(Thread).run() line: 745
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
if (SecurityContextHolder.getContext().getAuthentication() == null) {
SecurityContextHolder.getContext().setAuthentication(
createAuthentication((HttpServletRequest) req)); //创建Authentication
if (logger.isDebugEnabled()) {
logger.debug("Populated SecurityContextHolder with anonymous token: '"
+ SecurityContextHolder.getContext().getAuthentication() + "'");
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("SecurityContextHolder not populated with anonymous token, as it already contained: '"
+ SecurityContextHolder.getContext().getAuthentication() + "'");
}
}
chain.doFilter(req, res);
}
protected Authentication createAuthentication(HttpServletRequest request) {
AnonymousAuthenticationToken auth = new AnonymousAuthenticationToken(key,
principal, authorities);
auth.setDetails(authenticationDetailsSource.buildDetails(request));
return auth;
}
3、forward方式不会走过滤器,不会再创建授权信息Authentication,因此为null
标签中源码,JspAuthorizeTag.java
public int doStartTag() throws JspException {
try {
authorized = super.authorize();
if (!authorized && TagLibConfig.isUiSecurityDisabled()) {
pageContext.getOut().write(TagLibConfig.getSecuredUiPrefix());
}
if (var != null) {
pageContext.setAttribute(var, authorized, PageContext.PAGE_SCOPE);
}
return TagLibConfig.evalOrSkip(authorized);
}
catch (IOException e) {
throw new JspException(e);
}
}
AbstractAuthorizeTag.java
public boolean authorize() throws IOException {
boolean isAuthorized;
if (StringUtils.hasText(getAccess())) {
isAuthorized = authorizeUsingAccessExpression();
}
else if (StringUtils.hasText(getUrl())) {
isAuthorized = authorizeUsingUrlCheck();
}
else {
isAuthorized = false;
}
return isAuthorized;
}
public boolean authorizeUsingAccessExpression() throws IOException {
if (SecurityContextHolder.getContext().getAuthentication() == null) { //为null返回false,因此标签不起作用
return false;
}
SecurityExpressionHandler<FilterInvocation> handler = getExpressionHandler();
Expression accessExpression;
try {
accessExpression = handler.getExpressionParser().parseExpression(getAccess());
}
catch (ParseException e) {
IOException ioException = new IOException();
ioException.initCause(e);
throw ioException;
}
return ExpressionUtils.evaluateAsBoolean(accessExpression,
createExpressionEvaluationContext(handler));
}
/**
* Returns EVAL_BODY_INCLUDE if the authorized flag is true or UI security has been
* disabled. Otherwise returns SKIP_BODY.
*
* @param authorized whether the user is authorized to see the content or not
*/
public static int evalOrSkip(boolean authorized) {
if (authorized || DISABLE_UI_SECURITY) {
return Tag.EVAL_BODY_INCLUDE;
}
return Tag.SKIP_BODY; //跳过主题
}
四、解决方式:
1.这种问题可以不用解决,没有什么影响
本身登录界面就没有什么授权信息
2.跳转到登录界面时可以模拟AnonymousAuthenticationFilter创建Authentication信息,这个解决方案还没实现