login intercepter do not work in spring - 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?:(

Related

Getting value of RequestParam within an Interceptor

I hope you can help me. I have a Spring Interceptor to authorize users based on the URL configured in #RequestMapping of controller methods and the arguments (parameters) passed to the controller. All these request parameters are configured using the #RequestParam annotation. I need to retrieve the values passed from the #RequestParam within the Interceptor so that I can use those parameters to validate if the url has been accessed by the correct user and if the user is allowed to pass in the documentId. Please let me know if this is possible. When I do request.getParameter("documentId"), I dont get anything. I have some code as below
(Controller Method)
#RequestMapping(value = "/viewDocument.html")
public ModelAndView viewDocument(#RequestParam("documentId");
Intercept class
#Override
public boolean preHandle(final HttpServletRequest req, final HttpServletResponse resp, final Object handler) throws IOException {
if (handler instanceof HandlerMethod) {
final HandlerMethod handlerMethod = (HandlerMethod) handler;
final RequestMapping requstMapping = handlerMethod.getMethodAnnotation(RequestMapping.class);
if (requstMapping != null) {
final AuthorizeRequest authorizeRequestAnnotation = handlerMethod.getMethodAnnotation(AuthorizeRequest.class);
if (authorizeRequestAnnotation != null) {
try {
checkAccess(req, requstMapping, handlerMethod);
} catch (final SecurityException e) {
resp.sendError(HttpServletResponse.SC_FORBIDDEN, "You are not allowed to perform this function");
// return false;
} catch (final Exception e) {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST);
// return false;
}
}
}
}
return true;
}
private void checkAccess(final HttpServletRequest req, final RequestMapping requestMapping, final HandlerMethod handlerMethod) throws SecurityException {
final Map<String, Object> arguments = Maps.newHashMap();
final RequestMethod[] methods = requestMapping.method();
final MethodParameter[] methodParameters = handlerMethod.getMethodParameters();
for (final MethodParameter methodParameter : methodParameters) {
String parameterName = null;
final RequestParam requestParam = methodParameter.getParameterAnnotation(RequestParam.class);
if (requestParam != null) {
parameterName = requestParam.value();
arguments.put(parameterName, req.getParameter(parameterName));
}
}
final RuleValidator ruleValidator = rulesConfiguration.get(requestMapping.value()[0]);
ruleValidator.validate(arguments);
}
It is a GET method I am working with. Yes, If I remove the interceptor, documentId is sent. Below is my config for interceptors
<mvc:interceptors>
<bean class="mypackage.SecurityInterceptor" />
</mvc:interceptors>
Currently, I'm trying to achieve the same thing. So my last try:
request.getAttribute("org.springframework.web.servlet.HandlerMapping.uriTemplateVariables")
gives you parameter's value.
But I'm not sure, that this is right way.

Spring CookieLocaleResolver: set cookiePath

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

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>

How to test the destruction of session scoped beans in jUnit?

I have a session scoped bean in which I hold some user data and want to write a unit test to ensure that it remains session scoped.
I want to mock the starting and the ending of a session in jUnit and compare the values of the session scoped bean.
For now I have the following (rough drafts) of the unit test:
I use a custom context loader to register the session scope.
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.test.context.support.GenericXmlContextLoader;
import org.springframework.web.context.request.SessionScope;
public class SessionScopedGenericXmlContextLoader extends GenericXmlContextLoader {
#Override
protected void customizeBeanFactory(final DefaultListableBeanFactory beanFactory) {
beanFactory.registerScope("session", new SessionScope());
super.customizeBeanFactory(beanFactory);
}
}
The unit test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(loader = SessionScopedGenericXmlContextLoader.class,
locations = { "classpath:/test-applicationContext.xml","classpath:/cache-config.xml" })
public class CachingTest extends AbstractJUnit4SpringContextTests {
// Session scoping
protected MockHttpSession session;
protected MockHttpServletRequest request;
ApplicationContext ctx;
CacheManager cacheManager;
Cache accountSettingsCache;
#Autowired
#Qualifier("cachedMethods")
DummyCacheMethods methods;
#Autowired
private SecurityContextHolder contextHolder;
protected void startRequest() {
request = new MockHttpServletRequest();
request.setSession(session);
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(request));
}
protected void endRequest() {
((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).requestCompleted();
RequestContextHolder.resetRequestAttributes();
request = null;
}
protected void startSession() {
session = new MockHttpSession();
}
protected void endSession() {
session.clearAttributes();
session = null;
}
#Before
public void constructSession() {
ctx = applicationContext;
cacheManager = (CacheManager) ctx.getBean("cacheManager");
accountSettingsCache = cacheManager.getCache("accountSettingsCache");
startRequest();
startSession();
}
#After
public void sessionClean() {
endRequest();
endSession();
contextHolder.clearContext();
}
#Test
// #DirtiesContext
public void checkSession1() {
final Authentication authentication = new Authentication() {
public String getName() {
return "Johny";
}
public void setAuthenticated(final boolean isAuthenticated) throws IllegalArgumentException {
}
public boolean isAuthenticated() {
return true;
}
public Object getPrincipal() {
final CustomUserDetails pr = new CustomUserDetails();
pr.setUsername("Johny");
return pr;
}
public Object getDetails() {
return null;
}
public Object getCredentials() {
return null;
}
public Collection<GrantedAuthority> getAuthorities() {
return null;
}
};
contextHolder.getContext().setAuthentication(authentication);
assertTrue(methods.getCurrentUserName().equals(accountSettingsCache.get("currentUser").get()));
assertTrue(methods.getCurrentUserName().equals(((CustomUserDetails) authentication.getPrincipal()).getUsername()));
}
#Test
// #DirtiesContext
public void testSession2() {
final Authentication authentication2 = new Authentication() {
public String getName() {
return "James";
}
public void setAuthenticated(final boolean isAuthenticated) throws IllegalArgumentException {
}
public boolean isAuthenticated() {
return true;
}
public Object getPrincipal() {
final CustomUserDetails pr = new CustomUserDetails();
pr.setUsername("James");
return pr;
}
public Object getDetails() {
return null;
}
public Object getCredentials() {
return null;
}
public Collection<GrantedAuthority> getAuthorities() {
return null;
}
};
SecurityContextHolder.setContext(contextHolder.getContext());
SecurityContextHolder.clearContext();
SecurityContextHolder.getContext().setAuthentication(authentication2);
assertTrue(methods.getCurrentUserName().equals(accountSettingsCache.get("currentUser").get()));
assertTrue(methods.getCurrentUserName().equals(((CustomUserDetails) authentication2.getPrincipal()).getUsername()));
}
#Test
public void testWiring() {
assertTrue("cacheManager is null", cacheManager != null);
assertTrue("accountSettingsCache is null", accountSettingsCache != null);
}
}
And the relevant function from DummyCachedMethods is:
#Cacheable(value = { "accountSettingsCache" }, key = "new String(\"currentUser\")")
public String getCurrentUserName() {
return WebUtils.getCurrentUser().getUsername();
}
WebUtils.getCurrentUser() just returns the current Principal from the SecurityContext.
But this does not work, the test does not pass at this line:
assertTrue(methods.getCurrentUserName().equals(((CustomUserDetails) authentication2.getPrincipal()).getUsername()));
Since the method call is cached, it still returns Johnny instead of James.
My cache related beans are:
<bean id="accountSettingsCache" class="org.springframework.cache.concurrent.ConcurrentMapCache" scope="session">
<constructor-arg>
<value>accountSettingsCache</value>
</constructor-arg>
<aop:scoped-proxy proxy-target-class="false" />
</bean>
<bean id="defaultCache" class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="defaultCache" />
<bean id="cacheManager" class="my.package.DynamicCacheManager" scope="session">
<property name="caches">
<set>
<ref bean="accountSettingsCache" />
<ref bean="defaultCache" />
</set>
</property>
<aop:scoped-proxy />
</bean>
accountSettingsCache is session scoped, I want to start a new session and destroy it.
It does pass if I uncomment the DirtiesContext annotations, but I guess it's not what I want.
What am I missing ?

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