Spring CookieLocaleResolver: set cookiePath - spring

Is it possible to set cookiePath with the value of application name (automatically)?
For example I have a test.war so it will be available at bla.com/test/ so I want my cookie's path be /test/ and not / that is default value.
Thank you

When you create the CookieLocaleResolver you can set the path, but it will be hard coded.
Ex
<bean id="localeResolver" class="CookieLocaleResolver">
<property name="cookiePath" value="test" />
</bean>
Another possible solution is to override the LocaleResolver
public class MyCookieLocaleResolver extends CookieLocaleResolver {
#Override
public void setLocale(HttpServletRequest request,
HttpServletResponse response, Locale locale) {
if (locale != null) {
// Set request attribute and add cookie.
request.setAttribute(LOCALE_REQUEST_ATTRIBUTE_NAME, locale);
addCookie(response, locale.toString());
} else {
// Set request attribute to fallback locale and remove cookie.
request.setAttribute(LOCALE_REQUEST_ATTRIBUTE_NAME,
determineDefaultLocale(request));
removeCookie(response);
}
}
public void addCookie(HttpServletRequest request,
HttpServletResponse response, String cookieValue) {
Cookie cookie = createCookie(request, cookieValue);
Integer maxAge = getCookieMaxAge();
if (maxAge != null) {
cookie.setMaxAge(maxAge);
}
if (isCookieSecure()) {
cookie.setSecure(true);
}
response.addCookie(cookie);
if (logger.isDebugEnabled()) {
logger.debug("Added cookie with name [" + getCookieName()
+ "] and value [" + cookieValue + "]");
}
}
protected Cookie createCookie(HttpServletRequest request, String cookieValue) {
Cookie cookie = new Cookie(getCookieName(), cookieValue);
if (getCookieDomain() != null) {
cookie.setDomain(getCookieDomain());
}
cookie.setPath(request.getContextPath());
return cookie;
}
}

If you use a ServletContainer >= 2.5 and spring annotation, you can use the following code to get the ContextPath, so you don't have to hardcode:
#EnableWebMvc
#Configuration
#ComponentScan("com.controller")
public class WebConfig extends WebMvcConfigurerAdapter {
#Autowired
private ServletContext servletContext;
#Bean
public LocaleResolver localeResolver(){
CookieLocaleResolver resolver = new CookieLocaleResolver();
resolver.setDefaultLocale(new Locale("en"));
resolver.setCookieName("locale");
resolver.setCookiePath(servletContext.getContextPath());
resolver.setCookieMaxAge(31536000);
return resolver;
}
}

Related

login intercepter do not work in spring

all. i was using spring4 in my project. and add and interceptor extends HandlerInterceptorAdapter, then overwrite prehandle method. but i found it does not work when i was doing spring mock test.
i have configure it in springmvc-servlet.xml , like this:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.suerpay.common.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
and here is code of LoginInteceptor:
public class LoginInterceptor extends HandlerInterceptorAdapter {
#Autowired
LoginServiceRedis loginServiceRedis;
#Autowired
UserServiceDB userServiceDB;
Logger logger = LoggerFactory.getLogger(getClass());
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
logger.info("start login interceptor");
if (isLoginRequired(handler)) {
String ticket = request.getHeader(GlobalConstants.TICKET_HEADER);
if (StringUtils.isEmpty(ticket)) {
throw new UnAuthorizedException(ResultCodeConstants.USER_NOT_LOGIN);
}
String userName = loginServiceRedis.getUserNameByTicket(ticket);
Long userId = userServiceDB.getUserIdByName(userName);
if (null == userId) {
throw new UnAuthorizedException(ResultCodeConstants.USER_NOT_LOGIN);
}
ThreadContextHolder.setCurrentUserId(userId);
}
logger.info("finish login interceptor");
return true;
}
private boolean isLoginRequired(Object handler) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Method method = handlerMethod.getMethod();
LoginRequired loginRequired = method.getAnnotation(LoginRequired.class);
if (null != loginRequired) {
return true;
}
return false;
}
}
i think i have do everything , but just can not get into breakpoint.
who can tell me why?:(

Keep locale on Spring Security session timeout

I'm using Spring Security 3.0.2 and I have this config
<security:form-login
login-processing-url="/resources/j_spring_security_check"
login-page="/login"
authentication-failure-handler-ref="authErrorHandler" authentication-success-handler-ref="authCorrectHandler" />
<security:logout logout-success-url="/index.jsp" invalidate-session="false" />
<security:remember-me />
<security:session-management invalid-session-url="/login/sessionExpired" >
<security:concurrency-control max-sessions="1"
error-if-maximum-exceeded="true" />
</security:session-management>
When I login with a certain locale, all went well but when expiring session, Spring Security clear session and create new anonymous session with locale by default (and go to the login page as expected). The result is the user locale was LOST.
How can I keep the user locale when expiring session in Spring Security 3.0.2?
I'm using localeChangeInterceptor to set the locale, like this:
<bean id="localeChangeInterceptor" class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
<property name="paramName" value="language" />
</bean>
and SessionLocaleResolver as locale resolver:
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
<property name="defaultLocale" value="es" />
</bean>
EDIT - SOLVED FOR MY NEEDS
I've solved this finally setting a cookie in my own LocaleChangeInterceptor which extends from HandlerInterceptorAdapter, writing this in preHandle method:
LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);
if (localeResolver == null) {
throw new IllegalStateException("No LocaleResolver found.");
}
Cookie mylang = new Cookie("mylang", locale.getLanguage() + "_" + locale.getCountry());
mylang.setMaxAge(86400);
response.addCookie(mylang);
localeResolver.setLocale(request, response, locale);
and then in the /sessionExpired controller point, I'm getting the cookie value:
public String sessionExpired(HttpServletRequest request, HttpServletResponse response, Model model,
#CookieValue(value = "mylang", defaultValue = "es_ES") String myLang) throws Exception {
model.addAttribute("mylang", myLang);
LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);
StringTokenizer st = new StringTokenizer(myLang, "_");
String language = "";
String country = "";
try {
language = (String) st.nextElement();
country = (String) st.nextElement();
} catch (Exception e) {
throw new Exception("Error locale");
}
Locale locale = new Locale(language, country);
localeResolver.setLocale(request, response, locale);
return "sessionExpired";
No need to use database as temporal storage in this case.
Logically it does not look like you can use the session to keep track of the locale. The locale is set using a request param language which will be cleared with the session. When a new session is created the locale is defaulted to es as given by your localeResolver. One way I could think is to store user preferences in a DB and retrieve from there on subsequent log-ins
Also as suggested by #M. Deinum:
If you are ok with cookies use the CookieLocaleResolver which persists the choice in a cookie. Else implement your own LocaleResolver which stores/retrieves from database.
Stumbled upon this old question and just thought of sharing how I got away with my predicament using a Filter (would've preferred an Interceptor but for some reason it annoyingly ain't working for me as of this writing).
public class SessionTimeoutFilter implements Filter {
private String contextPath;
private String language;
#Override
public void init(FilterConfig config) {
contextPath = config.getInitParameter("context.path");
}
#Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
String path = request.getRequestURI();
if (path.equals(contextPath)) { //the index page
request.getSession(true);
chain.doFilter(request, response);
} else {
HttpSession session = request.getSession(false);
if (session != null) {
language = (String) session.getAttribute("lang");
chain.doFilter(request, response);
} else {
response.sendRedirect(contextPath + "?timeout=true&lang=" + language);
}
}
}
}
And on top of a LocaleChangeInterceptor, I had parameters in my Controller.
#GetMapping("/")
public String home(#RequestParam(value = "timeout", required = false) Boolean timeout,
#RequestParam(value = "lang", required = false) String language,
HttpServletRequest request, Model model) {
if (Strings.isNotBlank(language)) {
LocaleContextHolder.setLocale(new Locale(language));
} else {
language = LocaleContextHolder.getLocale().getLanguage();
}
if (BooleanUtils.isTrue(timeout)) {
String message = messageService.getMessage("error.timeout", null);
model.addAttribute("message", message);
}
request.getSession().setAttribute("lang", language);
return "index";
}

Spring REST Handle locale change

I'm trying to handle locale change in a Spring 3 REST application.
But the locale is not changed to fr.
The console log shows:
2014-05-19 14:29:46,214 DEBUG [AbstractExceptionHandler] locale: en
Here is my configuration:
#Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
messageSource.setBasenames("classpath:messages/messages", "classpath:messages/validation");
// If true, the key of the message will be displayed if the key is not found, instead of throwing an exception
messageSource.setUseCodeAsDefaultMessage(true);
messageSource.setDefaultEncoding("UTF-8");
// The value 0 means always reload the messages to be developer friendly
messageSource.setCacheSeconds(0);
return messageSource;
}
// The locale interceptor provides a way to switch the language in any page just by passing the lang=’en’, lang=’fr’, and so on to the url
#Override
public void addInterceptors(InterceptorRegistry registry) {
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("lang");
registry.addInterceptor(localeChangeInterceptor);
}
#Bean
public LocaleResolver localeResolver() {
CookieLocaleResolver localeResolver = new CookieLocaleResolver();
localeResolver.setDefaultLocale(new Locale("en"));
return localeResolver;
}
Here is my exception handler:
#ControllerAdvice
public class AdminExceptionHandler extends AbstractExceptionHandler {
#ExceptionHandler(NullPointerException.class)
#ResponseBody
public ResponseEntity<ErrorInfo> nullPointerException(HttpServletRequest request, NullPointerException e) {
String url = request.getRequestURL().toString();
String errorMessage = localizeErrorMessage("error.npe", new Object[] { e.getMessage() });
return new ResponseEntity<ErrorInfo>(new ErrorInfo(url, errorMessage), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
public class AbstractExceptionHandler {
private static Logger logger = LoggerFactory.getLogger(AbstractExceptionHandler.class);
#Autowired
private MessageSource messageSource;
protected String localizeErrorMessage(String errorCode, Object args[]) {
Locale locale = LocaleContextHolder.getLocale();
logger.debug("locale: " + locale);
return messageSource.getMessage(errorCode, args, locale);
}
protected String localizeErrorMessage(String errorCode) {
return localizeErrorMessage(errorCode, null);
}
protected String extractAdminIdFromUrl(String url) {
String adminId = null;
try {
URI uri = new URI(url);
String path = uri.getPath();
adminId = path.substring(path.lastIndexOf('/') + 1);
} catch (URISyntaxException e1) {
e1.printStackTrace();
}
return adminId;
}
}
And here is my test:
#Test
public void testExceptionLocalizedMessage() throws Exception {
HttpHeaders httpHeaders = Common.createAuthenticationHeaders("stephane" + ":" + PASSWORD);
MvcResult resultGet = this.mockMvc.perform(
get("/error/npe").headers(httpHeaders)
.param("lang", "fr")
.accept(MediaType.APPLICATION_JSON)
)
.andExpect(status().isInternalServerError())
.andExpect(jsonPath("$.message").value("Une erreur inconnue s'est produite. Veuillez nous excuser."))
.andReturn();
httpHeaders.add("Accept-Language", "fr");
resultGet = this.mockMvc.perform(
get("/error/npe").headers(httpHeaders)
.accept(MediaType.APPLICATION_JSON)
)
.andExpect(status().isInternalServerError())
.andExpect(jsonPath("$.message").value("Une erreur inconnue s'est produite. Veuillez nous excuser."))
.andReturn();
}
I would like to handle the locale argument in the url as in ?lang=en and the Accept-Language header as a fall back.
As a REST application I was thinking of using the AcceptHeaderLocaleResolver class but it does not support the setting of the locale via the url parameter.
I reckoned using the SessionLocaleResolver class makes little sense in a REST application.
That leaves my with the CookieLocaleResolver class which I'm not specially convinced is the one that should be used in a REST application.
Anyway, the retrieved locale is still en and not fr as I expect it to be.
EDIT:
In the test, using the statement:
httpHeaders.add("Accept-Language", Locale.FRENCH.getLanguage());
does not set the locale.
But using the locale() does.
This test passes:
this.mockMvc.perform(
get("/error/npe").headers(httpHeaders).locale(Locale.FRENCH)
.accept(MediaType.APPLICATION_JSON))
.andDo(print()
)
.andExpect(status().isInternalServerError())
.andExpect(jsonPath("$.message").value(localizeErrorMessage("error.npe", Locale.FRENCH)))
.andReturn();
I found the solution. Now the Accept-Language header is being used and the cookie as well.
public class WebConfiguration extends WebMvcConfigurerAdapter {
#Bean
public LocaleResolver localeResolver() {
return new SmartLocaleResolver();
}
}
public class SmartLocaleResolver extends CookieLocaleResolver {
#Override
public Locale resolveLocale(HttpServletRequest request) {
String acceptLanguage = request.getHeader("Accept-Language");
if (acceptLanguage == null || acceptLanguage.trim().isEmpty()) {
return super.determineDefaultLocale(request);
}
return request.getLocale();
}
}
UPDATE: Following Thor's comment, here is a resolver that checks first for the cookie, and if not found checks for the request header:
#Override
public Locale resolveLocale(HttpServletRequest request) {
Locale locale = super.determineDefaultLocale(request);
if (null == locale) {
String acceptLanguage = request.getHeader("Accept-Language");
if (acceptLanguage != null && !acceptLanguage.trim().isEmpty()) {
locale = request.getLocale();
}
}
return locale;
}
Or with a simpler implementation (not tested):
private AcceptHeaderLocaleResolver acceptHeaderLocaleResolver = new AcceptHeaderLocaleResolver();
#Override
public Locale resolveLocale(HttpServletRequest request) {
Locale locale = super.determineDefaultLocale(request);
if (null == locale) {
locale = acceptHeaderLocaleResolver.resolveLocale(request);
}
return locale;
}
UPDATE: This above solution is not working any longer.
I'm now trying to pass the accepted language in a header:
httpHeaders.add(HttpHeaders.ACCEPT_LANGUAGE, "fr_FR");
and retrieve it in this locale resolver:
#Override
public Locale resolveLocale(HttpServletRequest request) {
for (String httpHeaderName : Collections.list(request.getHeaderNames())) {
logger.debug("===========>> Header name: " + httpHeaderName);
}
String acceptLanguage = request.getHeader(HttpHeaders.ACCEPT_LANGUAGE);
logger.debug("===========>> acceptLanguage: " + acceptLanguage);
Locale locale = super.resolveLocale(request);
logger.debug("===========>> acceptLanguage locale: " + locale.getDisplayCountry());
if (null == locale) {
locale = getDefaultLocale();
logger.debug("===========>> Default locale: " + locale.getDisplayCountry());
}
return locale;
}
But there is no Accept-Language in the output of the ===========>> Header name logger, and the acceptLanguage logger is empty.
when we are using
#Bean
public SessionLocaleResolver localeResolver(){
SessionLocaleResolver localeResolver = new SessionLocaleResolver();
localeResolver.setDefaultLocale(Locale.US);
return localeResolver;
}
#Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("language");
return localeChangeInterceptor;
}
it is able to accept locale from query parameter
{{url}}/com-manh-cp-ext-order/api/ext/ex23order/greeting?language=es

Spring mvc - implementation of WebArgumentResolver

I wanted to create custom controller method argument annotation.
Following this question How to pass a session attribute as method argument (parameter) with Spring MVC and following #Bozho advice I have something like this:
my resolver
public class SessionAttributeAnnotationResolver implements WebArgumentResolver {
public Object resolveArgument(MethodParameter parameter,
NativeWebRequest request) throws Exception {
System.out.println("I am here");
Annotation[] parameterAnnotations = parameter.getParameterAnnotations();
Class<?> parameterType = parameter.getParameterType();
for (Annotation parameterAnnotation : parameterAnnotations) {
if (SessionAttribute.class.isInstance(parameterAnnotation)) {
SessionAttribute sessionAttribute = (SessionAttribute) parameterAnnotation;
String parameterName = sessionAttribute.value();
boolean required = sessionAttribute.required();
HttpServletRequest httprequest = (HttpServletRequest) request
.getNativeRequest();
HttpSession session = httprequest.getSession(false);
Object result = null;
if (session != null) {
result = session.getAttribute(parameterName);
}
if (result == null && required && session == null)
raiseSessionRequiredException(parameterName, parameterType);
if (result == null && required)
raiseMissingParameterException(parameterName, parameterType);
return result;
}
}
return WebArgumentResolver.UNRESOLVED;
}
protected void raiseMissingParameterException(String paramName,
Class<?> paramType) throws Exception {
throw new IllegalStateException("Missing parameter '" + paramName
+ "' of type [" + paramType.getName() + "]");
}
protected void raiseSessionRequiredException(String paramName,
Class<?> paramType) throws Exception {
throw new HttpSessionRequiredException(
"No HttpSession found for resolving parameter '" + paramName
+ "' of type [" + paramType.getName() + "]");
}
}
the annotation
#Target(ElementType.PARAMETER)
#Retention(RetentionPolicy.RUNTIME)
public #interface SessionAttribute {
String value();
boolean required() default true;
}
simple controller to test everything
#Controller
#RequestMapping("/test")
public class TestController {
#RequestMapping(method= RequestMethod.GET)
public String t(#SessionAttribute("userEntity") UserEntity e2,Model model,HttpServletRequest req){
System.out.println(req.getSession().getId());
UserEntity e=(UserEntity) req.getSession().getAttribute("userEntity");
System.out.println(e.getName());
System.out.println(e2.getName());
return "login";
}
}
and finally, Spring configuration
<beans:bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<beans:property name="customArgumentResolver" ref="sessionAttributeAnnotationResolver"/>
</beans:bean>
<beans:bean id="sessionAttributeAnnotationResolver" class="pl.meble.taboret.utils.SessionAttributeAnnotationResolver"/>
now, everything seems in order to me, but there is probably some silly mistake that I done, because when controller is executed, I am getting
F0B282C93B74F8FA3F21A51F46D4D4D5
username
null
With Spring 3.1.0 the ArgumentResolver has now changed to HandlerMethodArgumentResolver - prior to that it used to be WebArgumentResolver - a related answer is here
Once you have written a new HandlerMethodArgumentResolver which is not very different from your current implementation you can register it this way:
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean id="sessionAttributeAnnotationResolver" class="..SessionAttributeAnnotationResolver ">
</bean>
</mvc:argument-resolvers>
</mvc:annotation-driven>

Spring mvc 3 - HTTPS access

How can I force a page to be accessed via HTTPS only. Need to do this via Spring MVC 3 configuration file.
Spring-security has such a configuration. see here for how to do it. In short - you force the channel to use https:
<http>
<intercept-url pattern="/secure/**" access="ROLE_USER"
requires-channel="https"/>
<intercept-url pattern="/**" access="ROLE_USER"
requires-channel="any"/>
</http>
If you don't want to use spring-security, here's an interceptor that I wrote:
#Component
public class SslInterceptor extends HandlerInterceptorAdapter {
// no need to inject it for now..
private PathMatcher pathMatcher = new AntPathMatcher();
#Value("${base.url.secure}")
private String secureRoot;
#Resource(name="secureLocations")
private List<String> secureLocations;
#Value("${use.ssl}")
private boolean useSsl;
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
if (useSsl && !request.isSecure() && shouldForceSecure(request.getRequestURI())) {
String redirectUrl = secureRoot + request.getRequestURI();
if (request.getQueryString() != null) {
redirectUrl += "?" + request.getQueryString();
}
// force session creation - thus it will be accessible to both the
// secure and the insecure contexts
request.getSession(true);
response.sendRedirect(redirectUrl);
return false;
}
return true;
}
private boolean shouldForceSecure(String path) {
for (String pattern : secureLocations) {
if (pathMatcher.match(pattern, path)) {
return true;
}
}
return false;
}
}
For an annotation based approach without spring security I wrote an interceptor and a new annotation:
/**
* Request mapping annotation to enforce secure or insecure requests.
* Per default the annotated mapping is enforced to be secure.
*
* #see org.springframework.web.bind.annotation.RequestMapping
*/
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Mapping
public #interface RequestProtocol {
boolean secure() default true;
}
So you can simply declare a (here for REST) controller method like this:
#RequestMapping(value = "/secret", method = RequestMethod.GET)
#RequestProtocol(secure = true)
#ResponseBody
public Result doSecure(#Valid Model model) {
return doSomething(model));
}
To enable the mapping use an interceptor redirecting on the wrong protocol. You could also do a more simple handling by just sending a FORBIDDEN response.
/**
* Interceptor to send a redirect on security enforced mappings with wrong type of request.
*
* #see RequestProtocol
*/
class RequestProtocolInterceptor extends HandlerInterceptorAdapter {
private static final int PORT_DIFF = 443 - 80;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
Boolean secure = checkSecure(handler);
if (secure != null && request.isSecure() != secure) {
response.sendRedirect(switchSecure(secure, request.getRequestURL()));
return false;
}
return true;
}
private Boolean checkSecure(Object handler) {
if (handler instanceof HandlerMethod) {
HandlerMethod method = (HandlerMethod)handler;
RequestProtocol annotation = method.getMethodAnnotation(RequestProtocol.class);
if (annotation == null) {
annotation = AnnotationUtils.findAnnotation(method.getBeanType(), RequestProtocol.class);
}
return annotation == null ? null : annotation.secure();
}
return null;
}
private String switchSecure(boolean secure, StringBuffer url) {
int endSchema = url.indexOf("://");
url.replace(0, endSchema, secure ? "https" : "http");
int startPort = url.indexOf(":", endSchema + 3);
if (startPort != -1) {
int endPort = url.indexOf("/", startPort);
int port = Integer.parseInt(url.substring(startPort + 1, endPort));
port += secure ? PORT_DIFF : -PORT_DIFF;
url.replace(startPort + 1, endPort, String.valueOf(port));
}
return url.toString();
}
}
To enable the interceptor on a pure annotation based Spring config, use the WebMvcConfigurerAdapter:
#Configuration
#EnableWebMvc
public class MyConfiguration extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new RequestProtocolInterceptor());
}
}
You can do it in your Tomcat config.
try adding redirectPort="" in server.xml to the HTTP connector.
Hope it helps.
Update:
This article will explain you how to deal with SSL and has a lot of an examples.
http://tomcat.apache.org/tomcat-6.0-doc/ssl-howto.html

Resources