How can I add my own logout handler to LogoutFilter in spring-security ?
Thanks!
The following solution works for me and may be helpful:
Extend the SimpleUrlLogoutSuccessHandler or implement the LogoutHandler:
public class LogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
// Just for setting the default target URL
public LogoutSuccessHandler(String defaultTargetURL) {
this.setDefaultTargetUrl(defaultTargetURL);
}
#Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
// do whatever you want
super.onLogoutSuccess(request, response, authentication);
}
}
Add to your Spring Security Configuration:
<security:logout logout-url="/logout" success-handler-ref="logoutSuccessHandler" />
<bean id="logoutSuccessHandler" class="your.package.name.LogoutSuccessHandler" >
<constructor-arg value="/putInYourDefaultTargetURLhere" />
</bean>
See the answer in this post in the Spring Security Forum:
XML Definition:
<beans:bean id="logoutFilter" class="org.springframework.security.ui.logout.LogoutFilter">
<custom-filter position="LOGOUT_FILTER"/>
<beans:constructor-arg index="0" value="/logout.jsp"/>
<beans:constructor-arg index="1">
<beans:list>
<beans:ref bean="securityContextLogoutHandler"/>
<beans:ref bean="myLogoutHandler"/>
</beans:list>
</beans:constructor-arg>
</beans:bean>
<beans:bean id="securityContextLogoutHandler" class="org.springframework.security.ui.logout.SecurityContextLogoutHandler"/>
<beans:bean id="myLogoutHandler" class="com.whatever.CustomLogoutHandler">
<beans:property name="userCache" ref="userCache"/>
</beans:bean>
LogoutHandler class:
public class CustomLogoutHandler implements LogoutHandler {
private UserCache userCache;
public void logout(final HttpServletRequest request, final HttpServletResponse response, final Authentication authentication) {
// ....
}
#Required
public void setUserCache(final UserCache userCache) {
this.userCache = userCache;
}
}
You can use java-config solutions like this.
#Configuration
#EnableWebSecurity
public class SpringSecurity2Config extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
//you can set other security config by call http.XXX()
http
.logout()
.addLogoutHandler(new CustomLogoutHandler())
.logoutUrl("/logout")
.logoutSuccessHandler(...)
.permitAll();
}
static class CustomLogoutHandler implements LogoutHandler {
#Override
public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
//...
}
}
}
You should use success-handler-ref attribute of <logout> element:
<security:logout invalidate-session="true"
success-handler-ref="myLogoutHandler"
logout-url="/logout" />
As alternative solution you can configure your own filter on the logout URL.
Related
<bean class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler"/>
<security:http use-expressions="false" entry-point-ref="loginEntryPoint">
<security:custom-filter ref="customFormLoginFilter" position="FORM_LOGIN_FILTER"/>
<security:logout logout-url="/logout" logout-success-url="/login?logout=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>
<bean id="customFormLoginFilter" class="com.fetn.security.CustomAuthenticationFilter">
<property name="filterProcessesUrl" value="/login"/>
<property name="authenticationManager" ref="authenticationManager"/>
<property name="usernameParameter" value="custom_username"/>
<property name="passwordParameter" value="custom_password"/>
<property name="authenticationSuccessHandler">
<bean class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
<property name="defaultTargetUrl" value="/"/>
</bean>
</property>
<property name="authenticationFailureHandler">
<bean class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
<property name="defaultFailureUrl" value="/login/failure?error=true"/>
</bean>
</property>
</bean>
<bean id="loginEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
<constructor-arg value="/login"/>
</bean>
<security:authentication-manager alias="authenticationManager">
<security:authentication-provider ref="customAuthenticationProvider"/>
</security:authentication-manager>
I wrote belowJava Config Code but for logout and .antMatchers("/appointments/").access("hasRole('USER')") and antMatchers("/schedule/").access("hasRole('ADMIN')")
URL always go to /login/failure?error=true
what will be the appropriate java cofig code .Please Help.....
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{
#Autowired
private AutoUserRepository autoUserRepository;
#Autowired
private CustomAuthenticationProvider customAuthenticationProvider;
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(customAuthenticationProvider);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/appointments/*").access("hasRole('USER')").
antMatchers("/schedule/*").access("hasRole('ADMIN')").and().exceptionHandling().authenticationEntryPoint(loginEntryPoint()).and().addFilterBefore(customFormLoginFilter(), UsernamePasswordAuthenticationFilter.class);
http.logout().logoutUrl("/logout")
.logoutSuccessUrl("/login?logout=true");
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/resources/**");
}
#Bean
public DefaultWebSecurityExpressionHandler defaultWebSecurityExpressionHandler(){
return new DefaultWebSecurityExpressionHandler();
}
#Bean
public LoginUrlAuthenticationEntryPoint loginEntryPoint(){
LoginUrlAuthenticationEntryPoint ent=new LoginUrlAuthenticationEntryPoint("/login");
return ent;
}
#Bean
public CustomAuthenticationFilter customFormLoginFilter() throws Exception{
CustomAuthenticationFilter filter=new CustomAuthenticationFilter();
//setting up super class property AbstractAuthenticationProcessingFilter
filter.setFilterProcessesUrl("/login");//login url
filter.setAuthenticationManager(authenticationManagerBean());
filter.setUsernameParameter("custom_username");
filter.setPasswordParameter("custom_username");
filter.setAuthenticationSuccessHandler(savedRequestAwareAuthenticationSuccessHandler());
filter.setAuthenticationFailureHandler(simpleUrlAuthenticationFailureHandler());
return filter;
}
#Bean
public SavedRequestAwareAuthenticationSuccessHandler savedRequestAwareAuthenticationSuccessHandler(){
SavedRequestAwareAuthenticationSuccessHandler surl=new SavedRequestAwareAuthenticationSuccessHandler();
surl.setDefaultTargetUrl("/");//url after seuuces login
return surl;
}
#Bean
SimpleUrlAuthenticationFailureHandler simpleUrlAuthenticationFailureHandler(){
SimpleUrlAuthenticationFailureHandler faillure=new SimpleUrlAuthenticationFailureHandler();
faillure.setDefaultFailureUrl("/login/failure?error=true");
return faillure;
}
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
Could it be that you have to add .and() between the various antMatchers? Also you are using two http.* calls i think it can be done with one. See code below from this page.
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/home").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login")
.permitAll()
.and()
.logout()
.permitAll();
}
I am using a Spring Security, Restful WebService and Postman Rest Clien. i want to test my Restful WebService using the Postman without logging in and I do put a #Secured("AMIN_ROLE") to my methods for Restful webservice in order to secured the method, and using the POSTMAN Authorization tab i also put a Digest Auth which is the username and password with already MD5 format. now my problem is I cannot test my restful webservice using the Postman it will prompt a UNAUTHORIZED. please help me for this stuff...
security.xml
<security:http
use-expressions="true"
entry-point-ref="authEntryPoint">
<security:intercept-url pattern="/" access="permitAll" />
<security:intercept-url pattern="/index.jsp" access="permitAll" />
<security:intercept-url pattern="/**" access="isAuthenticated()" />
</security:http>
</security:http>
<security:authentication-manager alias="authManager">
<security:authentication-provider ref="authProvider"/>
</security:authentication-manager>
<bean id="authProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<property name="userDetailsService" ref="userDetailsService"/>
<property name="passwordEncoder" ref="md5Encoder"/>
</bean>
<bean id="md5Encoder" class="org.springframework.security.authentication.encoding.Md5PasswordEncoder"/>
<bean id="userDetailsService" class="com.shindi.ippan.service.UserAccountService"/>
UserAccountService.java
#Service("userAccountService")
public class UserAccountService extends AbstractService implements UserDetailsService{
#Autowired
#Qualifier("userAccountRepository")
IRepository<UserAccount> repository;
#Override
protected IRepository<UserAccount> getRepository() {
return repository;
}
#Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
UserAccount userAccount = ((UserAccountRepository)repository).readByUsername(username);
return userAccount;
}
}
UserAccount.java implements UserDetails
ApplicationAuthenticationEntryPoint.java
#Component("authEntryPoint")
public class ApplicationAuthenticationEntryPoint implements AuthenticationEntryPoint {
#Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException ae) throws IOException, ServletException {
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "MENSAHE UNAUTHORIZED");
}
}
web.xml
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
UserAccountController.java
#Controller
#RequestMapping("/userAccount")
public class UserAccountController extends AbstractController {
#Autowired
#Qualifier("userAccountService")
IService<UserAccount> service;
#Override
public IService<UserAccount> getService() {
return service;
}
#RequestMapping(value="/login", method=RequestMethod.POST)
public #ResponseBody UserAccount login( String username, String password ){
UserAccount userAccount = ((UserAccountService)service).loadUserByUsername(username);
return userAccount;
}
#Secured("ADMIN_ROLE")
#RequestMapping(value="/test", method=RequestMethod.POST)
public #ResponseBody String test(){
return "test...";
}
#RequestMapping(value="/denied", method=RequestMethod.POST)
public #ResponseBody String denied(){
return "denied...";
}
}
I am using java config for spring security and I am trying to replace this code with no luck
<security:custom-filter ref="authenticationTokenProcessingFilter" position="FORM_LOGIN_FILTER" />
cant find any info about how to use the position in java config
Update i am trying to replace this code by java config but with no luck
<security:http
realm="Protected API"
use-expressions="true"
auto-config="false"
create-session="stateless"
entry-point-ref="unauthorizedEntryPoint"
authentication-manager-ref="authenticationManager">
<security:custom-filter ref="authenticationTokenProcessingFilter" position="FORM_LOGIN_FILTER" />
<security:intercept-url pattern="/rest/user/authenticate" access="permitAll" />
<security:intercept-url method="GET" pattern="/rest/news/**" access="hasRole('user')" />
<security:intercept-url method="PUT" pattern="/rest/news/**" access="hasRole('admin')" />
<security:intercept-url method="POST" pattern="/rest/news/**" access="hasRole('admin')" />
<security:intercept-url method="DELETE" pattern="/rest/news/**" access="hasRole('admin')" />
</security:http>
<bean id="unauthorizedEntryPoint" class="net.dontdrinkandroot.example.angularrestspringsecurity.rest.UnauthorizedEntryPoint" />
<bean class="net.dontdrinkandroot.example.angularrestspringsecurity.rest.AuthenticationTokenProcessingFilter" id="authenticationTokenProcessingFilter">
<constructor-arg ref="userDao" />
</bean>
and this is my AuthenticationTokenProcessingFilter
public class AuthenticationTokenProcessingFilter extends UsernamePasswordAuthenticationFilter
{
private final UserDetailsService userService;
public AuthenticationTokenProcessingFilter(UserDetailsService userService)
{
this.userService = userService;
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException
{
HttpServletRequest httpRequest = this.getAsHttpRequest(request);
String authToken = this.extractAuthTokenFromRequest(httpRequest);
String userName = TokenUtils.getUserNameFromToken(authToken);
if (userName != null) {
UserDetails userDetails = this.userService.loadUserByUsername(userName);
if (TokenUtils.validateToken(authToken, userDetails)) {
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpRequest));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
chain.doFilter(request, response);
}
private HttpServletRequest getAsHttpRequest(ServletRequest request)
{
if (!(request instanceof HttpServletRequest)) {
throw new RuntimeException("Expecting an HTTP request");
}
return (HttpServletRequest) request;
}
private String extractAuthTokenFromRequest(HttpServletRequest httpRequest)
{
/* Get token from header */
String authToken = httpRequest.getHeader("X-Auth-Token");
/* If token not found get it from request parameter */
if (authToken == null) {
authToken = httpRequest.getParameter("token");
}
return authToken;
}
Hope this is clearer
Here are the filter classes in the order of execution and with the addFilter method of the HttpSecurity class you add your own filters:
#Override
public void configure(HttpSecurity http) throws Exception {
http.addFilter(new AuthenticationTokenProcessingFilter());
...
You have to either extend or provide an instance of the defined Spring filters. The order is based on the class or superclass so you don't have to add the position:
JavaDoc
I'm running in my web app filter, which recieves from external source info about user, if he's logged in or not. Heres my filter:
#Override
public void doFilter( ServletRequest request, ServletResponse response,
FilterChain chain ) throws IOException, ServletException
{
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
String loginBean = httpRequest.getHeader( CommonVariables.LOGIN_BEAN );
if ( loginBean == null )
{
System.out.println( "FILTER-----------" );
try
{
String login;
String domain;
//Here i'm getting login and domain string
loginBean = domain + "\\" + login;
httpResponse.addHeader( "LoginBean", loginBean );
System.out.println( login + " " + domain );
} catch ( Exception e )
{
e.printStackTrace();
//redirect to login page
httpResponse.sendRedirect( "..." );
return;
}
}
chain.doFilter( request, response );
}
Not I though that those header will be passed into next filters. Therefore I implemented Spring Security PRE_AUTH_FILTER:
Spring security context
<http use-expressions="true" auto-config="false" entry-point-ref="http403EntryPoint">
<!-- Additional http configuration omitted -->
<custom-filter position="PRE_AUTH_FILTER" ref="siteminderFilter" />
</http>
<beans:bean id="siteminderFilter" class=
"org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter">
<beans:property name="principalRequestHeader" value="LoginBean"/>
<beans:property name="authenticationManager" ref="authenticationManager" />
</beans:bean>
<beans:bean id="preauthAuthProvider"
class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<beans:property name="preAuthenticatedUserDetailsService">
<beans:bean id="userDetailsServiceWrapper"
class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<beans:property name="userDetailsService" ref="userDetailsService"/>
</beans:bean>
</beans:property>
</beans:bean>
<beans:bean id="http403EntryPoint" class="org.springframework.security.web.authentication.Http403ForbiddenEntryPoint"/>
<beans:bean id="userDetailsService" class="com.execon.security.CustomUserDetailsService"/>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="preauthAuthProvider" />
</authentication-manager>
Then I tried to parse loginBean String in my CustoUserDetailsService and receive actuall user object. But It is not fired, and app fails with this:
org.springframework.security.web.authentication.preauth.PreAuthenticatedCredentialsNotFoundException: LoginBean header not found in request.
So that means the header is set wrong? Or not set at all? What might be wrong?
Filter setting LoginBean is first one fired, then goes Spring SEcurity. Standard output works ok as I have:
17:12:15,669 INFO [stdout] (http--127.0.0.1-8080-2) FILTER-----------
17:12:15,669 INFO [stdout] (http--127.0.0.1-8080-2) LOGIN DOMAIN
You are setting something in the response and the Spring's class is looking for the same in the request.
The only way you can modify an incoming HttpServletRequest is to decorate it. You should define a class as follows first:
public class AuthHttpServletRequest extends HttpServletRequestWrapper
{
private String loginBean;
public AuthHttpServletRequest(HttpServletRequest aRequest, String loginBean)
{
super(aRequest);
this.loginBean = loginBean;
}
#Override
public String getHeader(String headerName)
{
if(CommonVariables.LOGIN_BEAN.equals(headerName)) {
return this.loginBean;
}
return super.getHeader(headerName);
}
}
Then, replace the following line in your filter:
httpResponse.addHeader( "LoginBean", loginBean );
with this:
request = new AuthHttpServletequest(httpRequest, loginBean);
Then your chain.doFilter gets the request that can return the loginBean as you intended it to, to the Spring's authentication filter class, down in the filter chain.
My system is Spring MVC based and I checked that Spring automatically sets PRAGMA: no-cache. The system is available to the users through SSL. When the users try to download something using the INTERNET EXPLORER 7 or 8 an error like "Internet Explorer cannot download file from server" appears (more details: http://support.microsoft.com/default.aspx?scid=KB;EN-US;q316431&).
I tried to configure the WebContentInterceptor like the code bellow but does not work:
<mvc:interceptors>
<bean id="webContentInterceptor" class="org.springframework.web.servlet.mvc.WebContentInterceptor">
<property name="cacheSeconds" value="2100" />
<property name="useExpiresHeader" value="false" />
<property name="useCacheControlHeader" value="false" />
<property name="useCacheControlNoStore" value="false" />
</bean>
</mvc:interceptors>
What can I do avoid Spring send the Pragma: no-cache and related to Cache Control?
Regards!
You can write your own custom interceptor and set the header values to the response object. Interceptors are nothing but filters so override the filter and use the
prehandle and posthandle to set the request and response headers respectively.
Let me know if you want specific examples doing that.
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**" />
<beans:bean id="customInterceptor"
class="org.example.interceptors.CustomInterceptor">
</beans:bean>
</mvc:interceptor>
</mvc:interceptors>
public class CustomInterceptor implements HandlerInterceptor{
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object arg2, ModelAndView modelAndView) throws Exception {
response.setHeader(...);}
}
The simplest approach is probably just to stop the header from being written with a servlet filter. This way no Spring configuration has to be modified and you pick up the correct cache functionality for free.
public class PragmaFilter implements Filter {
private static String PRAGMA_HEADER = "Pragma";
#Override
public void init(FilterConfig filterConfig) throws ServletException { }
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
chain.doFilter(request, new NoPragmaHttpServletResponseWrapper(response));
}
#Override
public void destroy() { }
private final class NoPragmaHttpServletResponseWrapper extends HttpServletResponseWrapper {
private NoPragmaHttpServletResponseWrapper(ServletResponse response) {
super((HttpServletResponse) response);
}
#Override
public void addHeader(String name, String value) {
if (PRAGMA_HEADER.equals(name)) {
return;
}
super.addHeader(name, value);
}
#Override
public void setHeader(String name, String value) {
if (PRAGMA_HEADER.equals(name)) {
return;
}
super.setHeader(name, value);
}
}
}
Try to set cache seconds to an negative value.
If this does not help you will need to override:
protected final void preventCaching(HttpServletResponse response)
or
protected final void applyCacheSeconds(HttpServletResponse response, int seconds, boolean mustRevalidate)
Both methods are implements in WebContentGenerator
<mvc:interceptors>
<bean id="webContentInterceptor" class="org.springframework.web.servlet.mvc.WebContentInterceptor">
<property name="cacheSeconds" value="2100" />
<property name="useExpiresHeader" value="false" />
<property name="useCacheControlHeader" value="false" />
<property name="useCacheControlNoStore" value="false" />
<property name="cacheMappings">
<props>
<prop key="/**/**">-1</prop><!-- removes pragma no-cache -->
</props>
</property>
</bean>
</mvc:interceptors>