/* * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.security.samples.servletapi.mvc; import java.io.IOException; import java.security.Principal; import javax.naming.AuthenticationException; import javax.servlet.AsyncContext; import javax.servlet.Servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.web.access.ExceptionTranslationFilter; import org.springframework.stereotype.Controller; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.servlet.mvc.support.RedirectAttributes; /** * A Spring MVC Controller that demonstrates Spring Security's integration with the * standard Servlet API's. Specifically it demonstrates the following: * <ul> * <li>{@link #authenticate(HttpServletRequest, HttpServletResponse)} - Integration with * {@link HttpServletRequest#authenticate(HttpServletResponse)}</li> * <li>{@link #login(HttpServletRequest, HttpServletResponse, LoginForm, BindingResult)} - * Integration with {@link HttpServletRequest#login(String, String)}</li> * <li>{@link #logout(HttpServletRequest, HttpServletResponse, RedirectAttributes)} - Integration with * {@link HttpServletRequest#logout()}</li> * <li>{@link #remoteUser(HttpServletRequest)} - Integration with * {@link HttpServletRequest#getRemoteUser()}</li> * <li>{@link #userPrincipal(HttpServletRequest)} - Integration with * {@link HttpServletRequest#getUserPrincipal()}</li> * <li>{@link #authentication(Authentication)} - Spring MVC's ability to resolve the * {@link Authentication} since it is found on * {@link HttpServletRequest#getUserPrincipal()}</li> * </ul> * * @author Rob Winch * */ @Controller public class ServletApiController { /** * Demonstrates that {@link HttpServletRequest#authenticate(HttpServletResponse)} will * send the user to the log in page configured within Spring Security if the user is * not already authenticated. * * @param request * @param response * @return * @throws ServletException * @throws IOException */ @RequestMapping("/authenticate") public String authenticate(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { boolean authenticate = request.authenticate(response); return authenticate ? "index" : null; } /** * Demonstrates that you can authenticate with Spring Security using * {@link HttpServletRequest#login(String, String)}. * * <p> * If we fail to authenticate, a {@link ServletException} is thrown that wraps the * original {@link AuthenticationException} from Spring Security. This means we can * catch the {@link ServletException} to display the error message. Alternatively, we * could allow the {@link ServletException} to propegate and Spring Security's * {@link ExceptionTranslationFilter} would catch it and process it appropriately. * </p> * <p> * In this method we choose to use Spring MVC's {@link ModelAttribute} to make things * easier for our form. However, this is not necessary. We could have just as easily * obtained the request parameters from the {@link HttpServletRequest} object. * Remember all of these examples would work in a standard {@link Servlet} or anything * with access to the {@link HttpServletRequest} and {@link HttpServletResponse}. * </p> * * @param request * @param response * @param loginForm * @param result * @return * @throws ServletException */ @RequestMapping(value = "/login", method = RequestMethod.POST) public String login(HttpServletRequest request, HttpServletResponse response, @ModelAttribute LoginForm loginForm, BindingResult result) throws ServletException { try { request.login(loginForm.getUsername(), loginForm.getPassword()); } catch (ServletException authenticationFailed) { result.rejectValue(null, "authentication.failed", authenticationFailed.getMessage()); return "login"; } return "redirect:/"; } /** * Demonstrates that invoking {@link HttpServletRequest#logout()} will log the user * out. Note that the response does not get processed, so you need to write something * to the response. * @param request * @param response * @param redirect * @return * @throws ServletException */ @RequestMapping("/logout") public String logout(HttpServletRequest request, HttpServletResponse response, RedirectAttributes redirect) throws ServletException { request.logout(); return "redirect:/"; } /** * Demonstrates Spring Security with {@link AsyncContext#start(Runnable)}. Spring * Security will automatically transfer the {@link SecurityContext} from the thread * that {@link AsyncContext#start(Runnable)} is invoked to the new Thread that invokes * the {@link Runnable}. * @param request * @param response */ @RequestMapping("/async") public void asynch(HttpServletRequest request, HttpServletResponse response) { final AsyncContext async = request.startAsync(); async.start(new Runnable() { public void run() { Authentication authentication = SecurityContextHolder.getContext() .getAuthentication(); try { final HttpServletResponse asyncResponse = (HttpServletResponse) async .getResponse(); asyncResponse.setStatus(HttpServletResponse.SC_OK); asyncResponse.getWriter().write(String.valueOf(authentication)); async.complete(); } catch (Exception e) { throw new RuntimeException(e); } } }); } /** * Demonstrates that Spring Security automatically populates * {@link HttpServletRequest#getRemoteUser()} with the current username. * @param request * @return */ @ModelAttribute("remoteUser") public String remoteUser(HttpServletRequest request) { return request.getRemoteUser(); } /** * Demonstrates that Spring Security automatically populates * {@link HttpServletRequest#getUserPrincipal()} with the {@link Authentication} that * is present on {@link SecurityContextHolder#getContext()} * @param request * @return */ @ModelAttribute("userPrincipal") public Principal userPrincipal(HttpServletRequest request) { return request.getUserPrincipal(); } /** * Spring MVC will automatically resolve any object that implements {@link Principal} * using {@link HttpServletRequest#getUserPrincipal()}. This means you can easily * resolve the {@link Authentication} just by adding it as an argument to your MVC * controller. Alternatively, you could also have an argument of type * {@link Principal} which would not couple your controller to Spring Security. * @param authentication * @return */ @ModelAttribute public Authentication authentication(Authentication authentication) { return authentication; } @RequestMapping("/") public String welcome() { return "index"; } @RequestMapping(value = "/login", method = RequestMethod.GET) public String login(@ModelAttribute LoginForm loginForm) { return "login"; } }