I am working on a Spring-MVC application in which we are using Spring-Security for authentication and authorization. In our application we send out emails with URL's. Many times the user is not logged in, but after login, we would want to redirect to the original link after login. Thank you. Tried XML configuration, but it is not working.
Xml config :
<!-- Global Security settings -->
<security:http pattern="/resources/**" security="none"/>
<security:http create-session="ifRequired" use-expressions="true" auto-config="false" disable-url-rewriting="true">
<security:form-login login-page="/login" username-parameter="j_username" password-parameter="j_password"
login-processing-url="/j_spring_security_check" default-target-url="/canvaslisting"
always-use-default-target="true" authentication-failure-url="/denied"/>
<security:remember-me key="_spring_security_remember_me" user-service-ref="userDetailsService"
token-validity-seconds="1209600" data-source-ref="dataSource"/>
<security:logout delete-cookies="JSESSIONID" invalidate-session="true" logout-url="/j_spring_security_logout"/>
<security:intercept-url pattern="/**" requires-channel="https"/>
<security:port-mappings>
<security:port-mapping http="8080" https="8443"/>
</security:port-mappings>
<security:logout logout-url="/logout" logout-success-url="/" success-handler-ref="myLogoutHandler"/>
<security:session-management session-fixation-protection="newSession">
<security:concurrency-control session-registry-ref="sessionReg" max-sessions="5" expired-url="/login"/>
</security:session-management>
</security:http>
<beans:bean id="sessionReg" class="org.springframework.security.core.session.SessionRegistryImpl"/>
<beans:bean id="rememberMeAuthenticationProvider"
class="org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices">
<beans:constructor-arg index="0" value="_spring_security_remember_me"/>
<beans:constructor-arg index="1" ref="userDetailsService"/>
<beans:constructor-arg index="2" ref="jdbcTokenRepository"/>
<property name="alwaysRemember" value="true"/>
</beans:bean>
<beans:bean id="jdbcTokenRepository"
class="org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl">
<beans:property name="createTableOnStartup" value="false"/>
<beans:property name="dataSource" ref="dataSource"/>
</beans:bean>
<!-- Remember me ends here -->
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider user-service-ref="LoginServiceImpl">
<security:password-encoder ref="encoder"/>
</security:authentication-provider>
</security:authentication-manager>
<beans:bean id="encoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder">
<beans:constructor-arg name="strength" value="11"/>
</beans:bean>
<beans:bean id="daoAuthenticationProvider"
class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="LoginServiceImpl"/>
<beans:property name="passwordEncoder" ref="encoder"/>
</beans:bean>
<beans:bean id="authenticationFilter"
class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
<beans:property name="filterProcessesUrl" value="/login" />
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="authenticationSuccessHandler">
<beans:bean class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<beans:property name="useReferer" value="true"/>
</beans:bean>
</beans:property>
<beans:property name="authenticationFailureHandler">
<beans:bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<beans:property name="defaultFailureUrl" value="/login?login_error=t" />
</beans:bean>
</beans:property>
</beans:bean>
</beans>
Thank you.
Although you have not shown how you have configured your tag, so I am assuming that, you already have, that see bellow code.
<security:http auto-config="true" use-expressions="false">
<security:form-login login-page="/login" login-processing-url="/login"
username-parameter="custom_username"
password-parameter="custom_password"
default-target-url="/appointments/"
always-use-default-target="true"
authentication-failure-url="/login?error=true"
/>
<security:intercept-url pattern="/appointments/*" access="ROLE_USER"/>
<security:intercept-url pattern="/schedule/*" access="ROLE_FOO"/>
<security:intercept-url pattern="/**" access="ROLE_ANONYMOUS, ROLE_USER"/>
</security:http>
so, if you are not mentioning these two lines
default-target-url="/appointments/"
always-use-default-target="true"
, then by default spring security will redirect you user to the same page (which he requested), and got authentication prompt.
If this still does not full fills your requirement then you may need to implement your own filter or take a look at FilterSecurityInterceptor or MethodSecurityInterceptor .
Bellow is an Example, which will help you to achieve your goal:
To understand this problem better, take a look on below example:
- user receives a newsletter mail where he's invited to vote for more beautiful holidays picture. The URL contains a hash parameter used to authenticate user in the page.
- when user clicks on given URL, it will arrive to voting page as already authenticated user.
So My Custom Filter Will be like bellow:
package com.osigu.ehr.config;
public class OneShotActionFilter extends GenericFilterBean {
private static final Logger LOGGER =
LoggerFactory.getLogger(OneShotActionFilter.class);
private static Map<String, String> users = new HashMap<String, String>();
static {
users.put("0000000000001", "bartosz");
users.put("0000000000002", "admin");
users.put("0000000000003", "mod");
}
private static final String PARAM_NAME = "uio";
private AuthenticationManager authenticationManager;
private UserDetailsService userDetailsService;
private final RedirectStrategy redirectStrategy = new
DefaultRedirectStrategy();
private enum AuthenticationStates {
REDIRECT, CONTINUE;
}
public void setAuthenticationManager(AuthenticationManager
authenticationManager) {
this.authenticationManager = authenticationManager;
}
public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain filterChain) throws IOException,
ServletException {
LOGGER.debug("One shot filter invoked");
if (attemptAuthentication(request) == AuthenticationStates.REDIRECT) {
// Note that we should handle that dynamically but for learning purposes
//we'll consider that one-shot
// authentication works only for this URL
this.redirectStrategy.sendRedirect((HttpServletRequest) request,
(HttpServletResponse) response,
"/secret/one-shot-action");
} else {
LOGGER.debug("User was not correctly authenticated, continue filter chain");
// continue execution of all other filters
// You can test the code without this fragment in the pages without ?uio parameter. You should see blank page because of
// security filter chain interruption.
filterChain.doFilter(request, response);
}
}
private AuthenticationStates attemptAuthentication(ServletRequest request) {
AuthenticationStates state = AuthenticationStates.CONTINUE;
Authentication authentication =
SecurityContextHolder.getContext().getAuthentication();
String code = request.getParameter(PARAM_NAME);
if ((authentication == null || !authentication.isAuthenticated()) && code !=
null &&
users.containsKey(code)) {
LOGGER.debug("Checking user for code " + code);
UserDetails user = userDetailsService.loadUserByUsername(users.get(code));
LOGGER.debug("Found user from code (" + users.get(code) + "). User found is " + user);
if (user != null) {
users.remove(code);
UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(user.getUsername(),
user.getPassword());
authentication = this.authenticationManager.authenticate(authRequest);
if (authentication != null && authentication.isAuthenticated()) {
SecurityContextHolder.getContext().setAuthentication(authentication);
state = AuthenticationStates.REDIRECT;
}
}
}
return state;
}
}
And Finally Configure it in your xml, like bellow
<security:http authentication-manager-ref="frontend" auto-config="true" use-
expressions="true" access-denied-page="/access-denied">
<-- other filters are defined here -->
<security:custom-filter ref="oneShootAuthFilter"
after="CONCURRENT_SESSION_FILTER"/>
</security:http>
<bean id="oneShootAuthFilter"
class="com.waitingforcode.security.filter.OneShotActionFilter">
<property name="authenticationManager" ref="frontend" />
<property name="userDetailsService" ref="inMemoryUserService" />
</bean>
To test the filter, we can try to access to http://localhost:8080/?uio=0000000000001. You should be redirected (but only once) to http://localhost:8080/secret/one-shot-action page
I wrote this example for better clarity and as reference for future questions.
Do up vote if you think it helped you to clear your doubt.
you can also visit this link