Spring Security 4拦截匿名用户AccessDeniedException

时间:2016-03-07 23:08:02

标签: spring-security

我的网站允许未经身份验证的(匿名)用户搜索需要身份验证才能查看的内容。目前,当他们单击链接以查看搜索结果页面上的某个内容项的详细信息时,Spring Security会正确地将用户标识为未经身份验证并显示登录页面。但是,我想介入并显示一个页面,以鼓励匿名用户注册该网站。我已经跟踪了过滤器链中发生的事情,但我不清楚是应该扩展现有的过滤器或处理程序还是创建自定义过滤器或处理程序。如果是后者,我不确定应该去哪里。

当我通过调试运行时,我可以看到以下情况:

  • ExceptionTranslationFilter.doFilter执行FilterSecurityInterceptor,确定详细信息页面需要身份验证(返回-1投票并抛出AccessDeniedException
  • ExceptionTranslationFilter捕获异常,确定用户是匿名用户并拨打authenticationEntryPoint,在这种情况下LoginUrlAuthenticationEntryPoint
  • LoginUrlAuthenticationEntryPoint调用重定向到登录页面的DefaultRedirectStrategy

所以,基本上我需要覆盖这个用例的重定向到登录页面。我最好的猜测是创建一个自定义过滤器,用于检查访问此特定详细信息页面的匿名用户的组合,并强制重定向到加入页面,在ExceptionTranslationFilter之后将过滤器插入链中。或者这对于处理单页重定向而言是否总有些过分,并且有更简单的方法来实现这一目标?

1 个答案:

答案 0 :(得分:1)

对于任何有兴趣的人,这里是自定义身份验证入口点的代码,借鉴LoginUrlAuthenticationEntryPointExceptionTranslationFilter

public class CustomAuthLoginEntryPoint extends LoginUrlAuthenticationEntryPoint {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private PortResolver portResolver = new PortResolverImpl();
    private AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
    private RequestCache requestCache = new HttpSessionRequestCache();
    private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
    private String joinPageUrl;

    public CustomAuthLoginEntryPoint(String loginFormUrl) {
        super(loginFormUrl);
    }

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
            AuthenticationException authException) throws IOException, ServletException {
        logger.debug("commence");

        String redirectUrl = null;

        if (!StringUtils.isBlank(joinPageUrl)) {
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();
            if (auth == null || trustResolver.isAnonymous(auth)) {
                SavedRequest savedRequest = requestCache.getRequest(request, response);
                redirectUrl = savedRequest.getRedirectUrl();

                if (redirectUrl.indexOf("viewDetail") > 0) {
                    String joinPageUrl = buildRedirectUrlToJoinPage(request);
                    logger.debug("Redirecting to '" + joinPageUrl + "'");
                    redirectStrategy.sendRedirect(request, response, joinPageUrl);
                    return;
                }               
            }
        }

        super.commence(request, response, authException);
    }

    protected String buildRedirectUrlToJoinPage(HttpServletRequest request) {

        int serverPort = portResolver.getServerPort(request);
        String scheme = request.getScheme();

        RedirectUrlBuilder urlBuilder = new RedirectUrlBuilder();

        urlBuilder.setScheme(scheme);
        urlBuilder.setServerName(request.getServerName());
        urlBuilder.setPort(serverPort);
        urlBuilder.setContextPath(request.getContextPath());
        urlBuilder.setPathInfo(joinPageUrl);

        return urlBuilder.getUrl();
    }

    public void setJoinPage(String joinPageUrl) {
        this.joinPageUrl = joinPageUrl;
    }
}

我将此添加到我的WebSecurityConfigurerAdapter

@Bean
public CustomAuthLoginEntryPoint customAuthLoginEntryPoint() {
    CustomAuthLoginEntryPoint entryPoint = new CustomAuthLoginEntryPoint("/user/login");
    entryPoint.setJoinPage("/user/join");
    return entryPoint;
}

和http configure:

    .exceptionHandling()
        .accessDeniedHandler(accessDeniedHandler())
        .authenticationEntryPoint(customAuthLoginEntryPoint())