Controller library direct access to HttpServletRequest - java

I have numerous controllers in my application that extend a central controller class. Currently, in every controller function, I have to pass the request into this function in order for my function to grab the current username. Is it possible for this controller class to get the request on it's own without requiring it as an extra parameter?
public class Controller {
protected String environment;
public Controller () {
}
public ModelAndView prepareModel (HttpServletRequest request, ModelAndView model) {
contentDao.clearExpiredLocks();
model.addObject("currentUser", contentDao.findUser(request.getRemoteUser()));
//define current environment
this.environment = (request.getRequestURL().indexOf("localhost") > 0) ? "dev" : "uat";
model.addObject("environment", this.environment);

You can get the current HttpServletRequest as follows:
HttpServletRequest request = (HttpServletRequest) RequestContextHolder
.currentRequestAttributes()
.resolveReference(RequestAttributes.REFERENCE_REQUEST);
You can use this code in a method of your controller, or use it to expose request as a request-scoped bean and inject the corresponding scoped proxy as a field of your controller.

You can use something like this:
public abstract class AbstractController {
protected HttpServletRequest req
protected AbstractController(HttpServletRequest req) {
this.req = req
}
}
public class ConcreteController extends AbstractController {
protected ConcreteController(String name) {
super(name);
}
private void getUserName(){
this.req.getRemoteUser();
}
}
That's just one quick tip, I believe that there are more possibilities how to do that.

In my case, What I did is : I get user MainController.getLoginPerson() and use all user's info in all controllers. All controllers extends to MainController.
Here is method MainController.getLoginPerson():
MainController.getLoginPerson() {
// calls to authentication service method
authenticationService.getLoginPerson();
}
authenticationService.getLoginPerson() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth != null) {
return (UserPrincipalImpl) auth.getPrincipal();
} else {
return null;
}
}

Related

Spring Rest Set Custom Object in Context

I am using Spring Rest for creating Rest APIs. For authentication, I have created a filter that extends from OncePerRequestFilter, this filter check if a valid token is present in the header. I have to set some custom object information in Spring context. So that I can retrieve it in my Controller classes. Something like:
AuthenticationFilter extends OncePerRequestFilter {
// validation goes here
requestContext.setSecurityContext(new SecurityContext() {
public Principal getUserPrincipal() {
return new UserInfo("", token, userID, userType);
}
}
});
Don't create custom filters for authentications. User security context instead it's pretty powerful.
I don't know about any option how put custom object into spring context. But I can show you how to create your custom context in spring framework and then use it as any other context. Of course you will be able to put custom objects into it.
PSEUDO CODE:
1.)
Create custom object which you want to have in context:
public class UserInfo implements Serializable {
private String id;
private String email;
//Getters and setters
}
2.)
Create custom context:
#Service
public class UserInfoContext {
private static final ThreadLocal<UserInfo> userInfoThreadLocal = new InheritableThreadLocal<>();
public void setUserInfo(UserInfo userInfo) {
UserInfoContext.userInfoThreadLocal.set(userInfo);
}
public UserInfo getUserInfo() {
return UserInfoContext.userInfoThreadLocal.get();
}
public void clearContext() {
UserInfoContext.userInfoThreadLocal.remove();
}
}
3.)
Create custom interceptor for initializing custom object inside custom context
#Service
public class UserInfoInterceptor implements HandlerInterceptor {
private UserInfoContext userInfoContext;
#Autowired
public UserInfoInterceptor(UserInfoContext userInfoContext) {
this.userInfoContext = userInfoContext;
}
#Override
public boolean preHandle(HttpServletRequest requestServlet, HttpServletResponse responseServlet, Object handler) throws Exception {
// Call userInfoContext.setUserInfo() with custom data.
return true;
}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) throws Exception {
userInfoContext.clearContext();
}
}
Thanks to ThreadLocal you are able to save custom information about current user.

SPRING #RestController: Passing Cookie to a Service from Every Method

I have a #RestController in which every method needs to pass a (WebSSO) cookie down to a service. The service in turn uses the cookie for authentication. I am autowiring the service bean in controller. The service has a setter setCredentials(String webSSOCookie). One easy way is to call this setter in every method. I would like to do it better way; for instance using HandlerInterceptor. However the HandlerInterceptor does not have access to the controller (and hence its members) - am I right?
In jersey I could use filter. How do I achieve it in SPRING?
#RestController
#RequestMapping("/documents")
public class ECMRestController {
#Autowired
public ECMService ecmService;
#RequestMapping(value="/{documentId}", method=RequestMethod.DELETE)
public void deleteDocument(#RequestParam("documentId") String documentId) throws IllegalArgumentException, HttpClientErrorException {
// I could get and pass the cookie to ecmService in every method.
// ecmService.setCredentials(webSSOCookieObtainedfromRequest);
// However I don't want to do it that way.
ecmService.deleteDocument(documentId);
}
// Other REST Methods that need to pass the cookie in the same way.
}
You can request the SecurityContextHolder to query the current Authentication that you had customized in a filter.
MyCustomAuth auth = (MyCustomAuth) SecurityContextHolder.getContext().getAuthentication();
auth.getCookie();
or you can just use ThreadLocal in a context that can be retrieve from anywhere:
public class CookieContext {
private static final ThreadLocal<Cookie> COOKIE = new ThreadLocal<>();
private static final CookieContext INSTANCE = new CookieContext();
public void setCookie(Cookie value) {
COOKIE.set(value);
}
public Cookie getCookie() {
return COOKIE.get();
}
public static CookieContext getContext() {
return INSTANCE;
}
}
public class CookieInterceptor extends HandlerInterceptorAdapter {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
CookieContext context = CookieContext.getContext();
context.setCookie(request.getCookies()[0]);
}
}

How to redirect to external url from service class?

In my current spring project, I am adding several pairs of classes (controller/service) to provide payment option through several payment services. Each one of this classes have a structure like that:
controller
#Controller
#RequestMapping(value = "pagseguro")
public class pagseguroPaymentController extends paymentController<Pagseguro> {
...
#Override
#RequestMapping(value = "comprar", method = RequestMethod.POST)
public void comprar(String payerId, String guid) throws Exception {
this.payment.comprar(payerId, guid);
}
...
}
service
#Service
public class pagseguroPaymentService extends paymentService<Pagseguro> {
...
#Override
public void comprar(String payerId, String guid) throws Exception {
...
String response = checkout.register(null, false);
}
...
}
in the method comprar from service class, I need redirect the application to an URL stored in a String variable (response in the example above).
My initial idea is use the library java.net from Java and create a utilitary class like that:
public class Redirect {
public static String url;
public static void redirect() {
...
}
}
Anyone can give me a hint about how to accomplish that?
To perform a simple redirection, you can inject the HttpServletResponse in your Controller layer:
#RequestMapping(value = "comprar", method = RequestMethod.POST)
public void comprar(String payerId, String guid, HttpServletResponse response) throws Exception {
this.payment.comprar(payerId, guid);
}
And then simply do a redirect using that object:
response.sendRedirect("http://newUrl");
Now - as the Luiggi mentions in the comment - I would not do the redirect itself in the service layer, but rather return a boolean which decides if the redirect needs to be done - and then do it here, in the controller layer.
Hope it helps.

Include login methods

I got an entity User and i want every method of every controller to have the access to the logged user without typing anything like :
model.addAttribute(userDao.getuser(principal.getUsername()));
You can implement a simple HandlerInterceptorAdapter that will add the user instance to the model after a handler is invoked.
class UserAddingHandlerInterceptor extends HandlerInterceptorAdapter {
// Autowire dependencies...
private static final String ATTRIBUTE = "user";
#Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
if (modelAndView != null && !modelAndView.getModelMap().hasAttribute(ATTRIBUTE) {
modelAndView.addObject(ATTRIBUTE, userDao.getuser(principal.getUsername()));
}
}
}

With Spring 3.1, How do you test urls for your controllers?

In prior versions of spring (3.0), it was possible to test your controllers via the correct urls using the RequestMappingHandlerAdapter and HandlerMapping objects in the ApplicationContext. However, in Spring 3.1, things have changed and the code I used to use to make this work no longer functions.
How do you test Spring controller urls in Spring 3.1? For example, I'd like to write code that looks like this:
ModelAndView modelAndView = handle("GET", "/businesses");
That way I'm testing my mappings in addition to the controller's action logic.
In particular, I am most interested in making sure that I can pass session attributes and have them correctly passed to my controller actions's #Valid annotation.
Is there any way to accomplish this with Spring 3.1?
This is the code I was using:
protected ModelAndView handle(HttpServletRequest request, HttpServletResponse response) throws Exception {
final HandlerMapping handlerMapping = applicationContext.getBean(RequestMappingHandlerMapping.class);
final HandlerExecutionChain handler = handlerMapping.getHandler(request);
assertNotNull("No handler found for request, check you request mapping", handler);
final Object controller = handler.getHandler();
final HandlerInterceptor[] interceptors = handlerMapping.getHandler(request).getInterceptors();
for (HandlerInterceptor interceptor : interceptors) {
final boolean carryOn = interceptor.preHandle(request, response, controller);
if (!carryOn) {
return null;
}
}
return handlerAdapter.handle(request, response, controller);
}
protected ModelAndView handle(String method, String path, String queryString) throws Exception {
request.setMethod(method);
request.setRequestURI(path);
if(queryString != null) {
String[] parameters = queryString.split("&");
for(String parameter : parameters) {
String[] pair = parameter.split("=");
if(pair.length == 2) {
request.setParameter(pair[0], pair[1]);
} else {
request.setParameter(pair[0], "");
}
}
}
return handle(request, response);
}
protected ModelAndView handle(String method, String path, String attribute, Object object) throws Exception {
MockHttpSession session = new MockHttpSession();
session.setAttribute(attribute, object);
request.setSession(session);
return handle(method, path, null);
}
protected ModelAndView handle(String method, String path) throws Exception {
return handle(method, path, null);
}
protected void assertContentType(ModelAndView modelAndView, String contentType) {
assertEquals(contentType, modelAndView.getView().getContentType());
}
Let me instead recommend spring-test-mvc which is currently in 1.0.0M1 but is planned to be packaged with newer Spring MVC versions. It should be able to handle the cases that you are looking for quite easily, your test would end up looking like this:
xmlConfigSetup("classpath:/META-INF/spring/web/webmvc-config.xml")
.configureWebAppRootDir("src/main/webapp", false).build()
.perform(get("/businesses").param("name", "param1"))
.andExpect(status().isOk())
.andExpect(view().name("viewname"));
Your test does look appropriate for 3.1, so if you still want to continue with your approach can you point exactly what is not working - it sounds like normal requests are going through but the session attributes don't seem to be binding?
This is one of the test cases I used with Spring 3.1. Hope it can meet your requirement.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({"file:src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml" })
public class ControllerTest {
#Autowired
private RequestMappingHandlerAdapter handleAdapter;
#Autowired
private RequestMappingHandlerMapping handlerMapping;
#Test
public void playerControllerTest() throws Exception{
MockHttpServletRequest request = new MockHttpServletRequest();
MockHttpServletResponse response = new MockHttpServletResponse();
request.setRequestURI("/players.show");
request.setMethod("GET");
Object handler = handlerMapping.getHandler(request).getHandler();
ModelAndView mav = handleAdapter.handle(request, response,handler);
ModelAndViewAssert.assertViewName(mav,"players");
}
}
Here is a very nice presentation which talks about testing Spring 3.1 classes and controller, following is an example:
#Test
public void testSave() {
Account account = new Account();
BindingResult result =
new BeanPropertyBindingResult(account, "account");
AccountManager mgr = createMock(AccountManager.class);
mgr.saveOrUpdate(account);
replay(mgr);
AccountController contrlr = new AccountController(mgr);
String view = contrlr.save(account, result);
assertEquals("redirect:accounts", view);
verify(mgr);
}
Hope that helps!

Categories