LWK69
LWK69

Reputation: 1080

Spring Security 4 intercept anonymous user AccessDeniedException

My website allows unauthenticated (anonymous) users to perform a search of content which requires authentication to view. Currently, when they click on a link to view the details of one of the content items on the search results page Spring Security correctly identifies the user as unauthenticated and displays the login page. However, I would like to intervene and instead display a page to encourage the anonymous user to sign up for the website. I have traced what is happening in the filter chain but it's not clear to me whether I should extend an existing filter or handler or create a custom filter or handler. If it's the later I'm not sure where it should go.

When I run this through debug I can see the following happening:

So, basically I need to override the redirection to the login page for this one use case. My best guess is to create a custom filter that checks for the combination of an anonymous user accessing this specific detail page and forces a redirect to the join up page, inserting the filter in the chain after ExceptionTranslationFilter. Or is this total overkill for handling a single page redirect and there's an easier way to accomplish this?

Upvotes: 0

Views: 871

Answers (1)

LWK69
LWK69

Reputation: 1080

For anyone interested, here's the code for the custom auth entry point, borrowing from LoginUrlAuthenticationEntryPoint and ExceptionTranslationFilter.

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;
    }
}

I added this to my WebSecurityConfigurerAdapter:

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

and the http configure:

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

Upvotes: 1

Related Questions