I am a newbie to Spring Security 3 . I am using roles for users to login.
I want to redirect a user to a different page based on the role of that user, I understand is that I would have to implement the AuthenticationSuccessHandler for the same, but some examples in that direction would help.
Thanks in advance,
Vivek
You can do something like this:
public class Test implements AuthenticationSuccessHandler {
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) {
Set<String> roles = AuthorityUtils.authorityListToSet(authentication.getAuthorities());
if (roles.contains("ROLE_USER") {
response.sendRedirect("/userpage");
}
}
}
In the XML config add this:
<bean id="authenticationFilter" class="YOUR_AUTH_FILTER_HERE">
<!-- There might be more properties here, depending on your auth filter!! -->
<property name="authenticationSuccessHandler" ref="successHandler" />
</bean>
<bean id="successHandler" class="Test"/>
Related
I am aware that swagger-ui can be fully disabled using #Profile on spring-boot application but I still want certain privileged user to be able to access swagger-ui and not fully disabled.
Is there a way to achieve this.
update:
currently I am using interceptor approach but i don't want this approach.
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
if(request.getRequestURI().contains("swagger") &&
!request.isUserInRole("XX_YY_ZZ")) {
response.sendError(403, "You are not authorized to access "); }
return super.preHandle(request, response, handler);
}
Without version you use, or codes, it is difficult to help. But I'll try as best as I can.
When you are using swagger-ui, you have an exposed URL to access your docs (usually, /swagger-ui.html).
You are using spring-boot, and talking about user restriction, so I assume you can use spring-boot-starter-security.
With spring-boot-starter-security, you can configure easily what URL you want to protect (regarding user roles for instance).
Here is a sample configuration that works:
#Configuration
#EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
// the rest of your configuration
http.authorizeRequests().mvcMatchers("/swagger-ui.html").hasRole("DEVELOPER")
}
You can secure swagger URLs just like any URLs you expose with your Controllers.
For more information:
A similar issue:
How to configure Spring Security to allow Swagger URL to be accessed without authentication
Spring security configuration:
https://spring.io/guides/gs/securing-web/
I could help more with:
An extract of your security configuration
The version of Spring-boot you're using
I would suggest adding an interceptor or you can handle it in your exiting interceptor if you have any.
In the spring configuration file:
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/swager-url" />
<ref bean="swagerInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
<bean id="swagerInterceptor" class="com.security.SwagerInterceptor">
<property name="userService" ref="userService" />
</bean>
The interceptor class can be written similar to this:
public class SwagerInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
//Take out user information from the request and validate
}
In a Spring MVC application I am trying to implement a custom logout success handler. This handler should access a session attribute and make some queries and logging bases on its value.
Relevant parts of the implementation:
Security configuration:
<http ...>
<logout success-handler-ref="logoutSuccessHandler"/>
</http>
<beans:bean id="logoutSuccessHandler" class="some.package.LogoutSuccessHandler">
<beans:constructor-arg value="/login" />
</beans:bean>
The handler itself:
public class LogoutSuccessHandler extends SimpleUrlLogoutSuccessHandler {
#Autowired
private SomeService someService;
public LogoutSuccessHandler(String defaultTargetUrl) {
this.setDefaultTargetUrl(defaultTargetUrl);
}
#Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
String sessionAttribute = request.getSession().getAttribute("someAttribute").toString();
someService.doSomething(sessionAttribute);
super.onLogoutSuccess(request, response, authentication);
}
}
I am adding some attributes to the session when a user logs in. They are visible during different controller requests. The problem is, when I try to access session attributes during logout, they are all gone. Does that mean that by that time the logout has already taken place and session information is wiped out? I can see that SessionId is the same as before though.
So, my question is: what happens to the session attributes and is there a way to access them in LogoutSuccessHandler?
<logout success-handler-ref="logoutSuccessHandler" invalidate-session="false"/>
the default value of invalidate-session is true, so you will get a new session in your handler.
When you set this value to false, then you can get the old session, and don't forget to invalidate session after you finished your business.
I'm working on a REST service provider web application with Java + Spring and I wonder if I can fuse my in-house authentication and authorization code with Spring Security?
To be more specific, I have two methods (that I like to keep):
User authenticate(String username, String password) { ... }
boolean authorize(User user, String resource) { ... }
In this scenario, a REST API is a resource and the second method verifies whether it can be called by the user or not.
My question is if I switch to Spring Security then should I give in to it and annotate my methods with user names and role names!? Or is there a way that I can keep my way of thinking (that REST APIs are resources and annotating them with user names and role names is like hard-coding ACL)?
I know my question is somewhat vague but I've been struggling with it for some time now and I'll be really grateful if someone can give a straight answer.
You can inject your own authentication manager with password encoder.
Below is a post where I posted some questions about it a while ago. It's not an exact answer for you but it will lead you down the right track.
debug spring security authentication-manager
At the time I did it xml style but I'm sure you can change this to a java config style.
It definitely worked foe me!
Spring security also very useful for providing authentication and authorization to the REST URLs. We no need to specify any custom implementations.
First, you need to specify the entry-point-ref to restAuthenticationEntryPoint in your security configuration as below.
<security:http pattern="/api/**" entry-point-ref="restAuthenticationEntryPoint" use-expressions="true" auto-config="true" create-session="stateless" >
<security:intercept-url pattern="/api/userList" access="hasRole('ROLE_USER')"/>
<security:intercept-url pattern="/api/managerList" access="hasRole('ROLE_ADMIN')"/>
<security:custom-filter ref="preAuthFilter" position="PRE_AUTH_FILTER"/>
</security:http>
Implementation for the restAuthenticationEntryPoint might be as below.
#Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException ) throws IOException {
response.sendError( HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized" );
}
}
After this you need to specify RequestHeaderAuthenticationFilter. It contains the RequestHeader key. This is basically used for identifying the user`s authentication. Generally RequestHeader carries this information while making the REST calls.
For example consider below code
<bean id="preAuthFilter" class="org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter">
<property name="principalRequestHeader" value="Authorization"/>
<property name="authenticationManager" ref="authenticationManager" />
</bean>
Here,
<property name="principalRequestHeader" value="Authorization"/>
"Authorization" is the the key presented the incoming request. It holds the required user`s authentication information.
Also you need to configure the PreAuthenticatedAuthenticationProvider to fulfill our requirement.
<bean id="preauthAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<property name="preAuthenticatedUserDetailsService">
<bean id="userDetailsServiceWrapper"
class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
<property name="userDetailsService" ref="authenticationService"/>
</bean>
</property>
</bean>
This code will work for securing the REST urls by means of Authentication and authorization without any custom implementations.
My suggestion is go with Spring-AOP for custom authentication and authorization, if you want custom implementation for authentication and authorization which works similar to the spring security.
For Complete code please find the below link:
https://github.com/srinivas1918/spring-rest-security
I hope that, i understood your question.
I am having a problem with SwitchUserFilter in Spring security. I have following configuration:
<bean id="ldapUserSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
<constructor-arg name="searchBase" value=""/>
<constructor-arg name="searchFilter" value="(uid={0})"/>
<constructor-arg name="contextSource" ref="ldapContext"/>
</bean>
<security:ldap-server id="ldapContext" url="ldap://xxxxxxx"/>
<bean id="ldapAuthProvider" class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
<constructor-arg name="authenticator">
<bean
class="org.springframework.security.ldap.authentication.BindAuthenticator">
<constructor-arg ref="ldapContext" />
<property name="userSearch" ref="ldapUserSearch" />
</bean>
</constructor-arg>
<constructor-arg name="authoritiesPopulator" ref="dbLDAPAuthPopulator" />
</bean>
<security:authentication-manager>
<security:authentication-provider ref="ldapAuthProvider"/>
</security:authentication-manager>
And the corresponding SwitchUserFilter bean is created as:
SwitchUserFilter switchUserFilter = new SwitchUserFilter();
switchUserFilter.setUserDetailsService(ldapUserDetailsService);
switchUserFilter.setTargetUrl("/");
switchUserFilter.setSwitchUserUrl("/impersonate");
switchUserFilter.setUsernameParameter("username");
switchUserFilter.setExitUserUrl("/unimpersonate");
When I go to the url "/impersonate" the user gets impersonated properly. However when the redirect is send to the target url i.e. "/" the user is again authenticated using basic auth.
I had a look at the code of both the SwitchUserFilter and BasicAuthenticationFilter and seems that SU will not work with basic auth.
This is what happens:
When the /impersonate?username=xyz url is called it goes to SwitchUserFilter which gets the details of xyz user from the ldap and it then sets the securitycontext in the session. Code snippet is as follows:
if (requiresSwitchUser(request)) {
// if set, attempt switch and store original
try {
Authentication targetUser = attemptSwitchUser(request);
// update the current context to the new target user
SecurityContextHolder.getContext().setAuthentication(targetUser);
// redirect to target url
successHandler.onAuthenticationSuccess(request, response, targetUser);
} catch (AuthenticationException e) {
logger.debug("Switch User failed", e);
failureHandler.onAuthenticationFailure(request, response, e);
}
return;
So in the SecurityContext you have information about xyz user.
Then when it redirects to target url i.e. "/" basicAuthenticationFilter is called which checks whether the user is authenticated. Code snippet:
Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication();
if(existingAuth == null || !existingAuth.isAuthenticated()) {
return true;
}
// Limit username comparison to providers which use usernames (ie UsernamePasswordAuthenticationToken)
// (see SEC-348)
if (existingAuth instanceof UsernamePasswordAuthenticationToken && !existingAuth.getName().equals(username)) {
return true;
}
// Handle unusual condition where an AnonymousAuthenticationToken is already present
// This shouldn't happen very often, as BasicProcessingFitler is meant to be earlier in the filter
// chain than AnonymousAuthenticationFilter. Nevertheless, presence of both an AnonymousAuthenticationToken
// together with a BASIC authentication request header should indicate reauthentication using the
// BASIC protocol is desirable. This behaviour is also consistent with that provided by form and digest,
// both of which force re-authentication if the respective header is detected (and in doing so replace
// any existing AnonymousAuthenticationToken). See SEC-610.
if (existingAuth instanceof AnonymousAuthenticationToken) {
return true;
}
return false;
As you can see it checks existingAuth.getName().equals(username)) which in this case it is xyz. However logged in user is different so the filter again authenticates the user and all the work done by SwitchUserFilter is overridden.
Is their any way to solve this issue? Can I override the BasicAuthenticationFilter?
This question is quite old, however should anyone come across it the answer is still valid today. You don't show your <http /> stanzas for Spring Security but you need to ensure that the role granted by impersonation is the same role (authority) required to bypass authentication at /*. If it's not then yes, you will be asked to authenticate.
You can specify custom authorities to be granted on impersonation by implementing an extension of SwitchUserAuthorityChanger and passing a reference of it to SwitchUserFilter.
I want to display custom error message in jsp for spring security authentication exceptions.
For wrong username or password,
spring displays : Bad credentials
what I need : Username/Password entered is incorrect.
For user is disabled,
spring displays : User is disabled
what I need : Your account is diabled, please contact administrator.
Do I need to override AuthenticationProcessingFilter just for this ? or else can I do something in jsp itself to find the authentication exception key and display different message
Redefine the properties in messages.properties inside spring security jar. For example add to the classpath myMessages.properties and add a message source to the context:
AbstractUserDetailsAuthenticationProvider.badCredentials=Username/Password entered is incorrect.
AbstractUserDetailsAuthenticationProvider.disabled=Your account is diabled, please contact administrator.
At Salvin Francis:
Add myMessages.properties to the WAR file inside WEB-INF/classes.
Add this bean to spring context config file
Message Source Bean
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basenames">
<list>
<value>myMessages</value>
</list>
</property>
</bean>
After adding the "messageSource" bean, I had problems to get the Error Message work with the CookieLocaleResolver because the DispatcherServlet (which does use this for your application automatically) is invoked after the Security.
See: http://static.springsource.org/spring-security/site/docs/3.1.x/reference/springsecurity-single.html#localization
My Solution was a custom Filter which sets the LocalContextHolder:
public class LocaleContextFilter extends OncePerRequestFilter {
private LocaleResolver localeResolver;
public void setLocaleResolver(LocaleResolver localeResolver) {
this.localeResolver = localeResolver;
}
#Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
// store Local into ThreadLocale
if (this.localeResolver != null) {
final Locale locale = this.localeResolver.resolveLocale(request);
LocaleContextHolder.setLocale(locale);
}
try {
filterChain.doFilter(request, response);
} finally {
LocaleContextHolder.resetLocaleContext();
}
}
}
And the Spring Security Context configuration:
<http use-expressions="true">
<custom-filter ref="localeContextFilter" after="FIRST" />
.....
</http>
<beans:bean id="localeContextFilter" class="at.telekom.ppp.util.opce.fe.interceptor.LocaleContextFilter" >
<beans:property name="localeResolver" ref="localeResolver" /><!-- e.g.: CookieLocaleResolver -->
</beans:bean>
I hope this helps others which has this problem.
Here is a JSP EL fix for this. More of a hack than an elegant solution, but gets the job done quick and dirty. Caveat- this is not i18n safe! Only English.
This requires the functions tag library:
<%# taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
And the replace code:
${fn:replace(SPRING_SECURITY_LAST_EXCEPTION.message, 'Bad credentials', 'Username/Password are incorrect')}
I am new to spring, but try this at the server:
throw new BadCredentialsException("This is my custom message !!");
Of course you need a class that is an authentication provider for this to work.