spring security authentication using ip address and username password - spring

I am using loadUserByUsername method to authenticate user, however, I need to validate against allowed ip addresses as well.
But when I am trying
SecurityContextHolder.getContext().getAuthentication();
getting null.
Please advice, how I can access users client ip address while authenticating user.

To solve your problem you should implement custom authentication provider (that can be based on DaoAuthenticationProvider or can be implemented from scratch, etc). This authentication provider should be registered in Authentication manager providers set. Also, this provider will have autowired HttpServletRequest type property, related to context http request. Then, when you performing client authenticationv via that provider, you can obtain user IP address by invoking HttpServletRequest.getRemoteAddr().
Code:
/**
* IP address based authentication provider
*/
#Service
public class IPAddressBasedAuthenticationProvider extends AuthenticationProvider {
/**
* Context http request
*/
#Autowired
private HttpServletRequest request;
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String ipAddress = request.getRemoteAddr();
//do authentication specific stuff (accessing users table in database, etc.)
//return created authentication object (if user provided valid credentials)
}
}
Configuration:
<security:http auto-config="true" authentication-manager-ref="authenticationManager" use-expressions="true"/>
<bean id="authenticationManager" class="org.springframework.security.authentication.ProviderManager">
<constructor-arg name="providers">
<list>
<ref bean="iPAddressBasedAuthenticationProvider"/>
</list>
</constructor-arg>
</bean>
Also, you can add other authentication providers (if you need to).
Hope this helps.
Links: AuthenticationProvider
ProviderManager

/**
* IP address based authentication provider
*/
#Service
public class IPAddressBasedAuthenticationProvider extends AuthenticationProvider {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
final WebAuthenticationDetails details = (WebAuthenticationDetails) auth.getDetails();
details.getRemoteAddress();
}
}

You are implementing UserDetailsService's loadUserByUsername method.
As per documentation
There is often some confusion about UserDetailsService. It is purely a DAO for user data and performs no other function other than to supply that data to other components within the framework. In particular, it does not authenticate the user, which is done by the AuthenticationManager. In many cases it makes more sense to implement AuthenticationProvider directly if you require a custom authentication process.
UserDetails userDetails= customUserDetailsService.loadUserByUsername("name");
this will give a userDetails object.You can do all authority related code in loadUserByUsername().If you would like to manually set an authenticated user in Spring Security.follow the code
Authentication authentication= new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()) ;
SecurityContextHolder.getContext().setAuthentication(authentication);
You will get IP address from request header.
How can I retrieve IP address from HTTP header in Java
you can do that somewhere in spring security filterchain.

Related

How to have Spring Security enabled for an application using third party login?

I have a Spring Boot enabled application whose login is controlled by third party Siteminder application. After successful authentication, Sitemeinder redirects to our application url. We fetch the HttpRequest from Siteminder and process the requests.
Now, how can Spring security be enabled in this case for authorizing users based on roles.
#Controller
public class LoginController
#RequestMapping( value= "/")
public void requestProcessor(HttpServletRequest request)
{
.
.
.}
The above controller's request mapper reads the request coming from SiteMinder and processes the request which has the Role of the user logged in. Where can we have Spring Security enabled to authorize pages and service methods to the user.
This is an scenario for the PreAuthenticated security classes:
Take a look here:
http://docs.spring.io/spring-security/site/docs/current/reference/html/preauth.html
Spring Security processes request before it gets to your controller in a filter configured in spring security configuration.
There is a documentation on how to configure spring security with SiteMinder.
The rules in your configuration will define the access to resources
Depends what you get in session. If somehow u can to take user and password from session you can authenticate user directly from code as :
#Autowired
AuthenticationManager authenticationManager;
...
public boolean autoLogin(String username, String password) {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
Authentication auth = authenticationManager.authenticate(token);
if (auth.isAuthenticated()) {
logger.debug("Succeed to auth user: " + username);
SecurityContextHolder.getContext().setAuthentication(auth);
return true;
}
return false;
}

Spring OAuth - Reload resourceIds and authorities of authentication

I just apply Spring Boot and Spring Cloud to build a microservice system. And I also apply Spring Oauth to it. Honestly, everything is perfect. Spring does a great job in it.
In this system, I have a microservice project does the job of an OAuth server, using JDBC datasource, and I using Permission based for UserDetails authorities (1 User has several Permissions). There are several microservice project does the jobs of Resource server (expose Rest api using Jersey), access security is based on Permissions of Authentication of OAuth bearer token.
Resource Server OAuth config class is something like this
#Configuration
#EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.csrf().disable();
http.authorizeRequests()
.antMatchers("/restservice/object/list")
.hasAuthority("PERMISSION_VIEW_OBJECT_LIST");
// ...
}
#Override
public void configure(ResourceServerSecurityConfigurer resources)
throws Exception {
resources.resourceId("abc-resource-id")
.tokenStore(new JdbcTokenStore(dataSource()));
}
#Bean
#ConfigurationProperties(prefix = "oauth2.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
}
Everything is great! But I encounter 2 problems:
If I add a new microservice project as a new resourceId, and I append resourceId value to RESOURCE_IDS in table OAUTH_CLIENT_DETAILS of the OAuth client, all requests to Rest API of new resource service return error something like this
{"error":"access_denied","error_description":"Invalid token does not contain resource id (xyz-resource-id)"}
This happens even when user logout and re-login to obtain new access token. It only works if I go to delete records of the Access token and Refresh token int table OAUTH_ACCESS_TOKEN and OAUTH_REFRESH_TOKEN in database.
If at runtime, Permission of a User is changed, the authorities of authentication is not reloaded, I see that AUTHENTICATION value of the Access Token in table OAUTH_ACCESS_TOKEN still contains old Authorities before Permission is changed. In this case, User must logout and re-login to obtain new Access Token with changed authorities.
So, are there any ways to fix these 2 problems.
I'm using Spring Cloud Brixton.SR4 and Spring Boot 1.3.5.RELEASE.
If you are using the default Spring JdbcTokenStore, then the users authentication is serialised and stored with the access/refresh token when the user authenticates and retrieves their token for the first time.
Each time the token is used to authenticate, it is this stored authentication that is loaded which is why changes to the user permissions or the addition of extra resources is not reflected in the users permissions.
In order to add in some checking on this, you can extend DefaultTokenServices and override the loadAuthentication(String accessTokenValue) method to perform your own checks once the users authentication is loaded from the token store.
This may not be the ideal way of doing this, but it is the only way we've found of doing it so far.
To override DefaultTokenServices, add the follwoing bean method to you AuthorizationServerConfigurerAdapter config class:
class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
#Bean
public AuthorizationServerTokenServices authorizationServerTokenServices() throws Exception {
// Where YourTokenServices extends DefaultTokenServices
YourTokenServices tokenServices = new YourTokenServices();
tokenServices.setTokenStore(tokenStore);
tokenServices.setClientDetailsService(clientDetailsService);
return tokenServices;
}
}
I resolved reload problem this way.
#Bean
public ClientDetailsService jdbcClientDetailsService() {
return new JdbcClientDetailsService(dataSource);
}

How to retrive user entered password in UserDetailsService

Spring security 3 may do some trick to validate user's password behind the scene, but that's become my problem right now, I am trying to intercept whatever entered for password by user, and just couldn't find a clue.
#Component("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService {
............
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException, DataAccessException {
User user = userService.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("User '"+username+"' not found !");
}
return user;
}
}
is there any API that I can use to intercept the user's password?
The UserDetailsService is responsible to load the user and provide a UserDetails object that contains the password stored in the database. Unfortunaly (for you) this password is hashed (SHA or MD5) in the most cases.
If you want to intercept the password that is entered by the user, then you have different choices:
The UserNamePasswordFilter (when you use Form Authentication, if you use an other kind of authentication, then you need an other filter) ins one point to intercept the password. It is responsible to fetch the login http request, create a UserNamePasswordAuthenticationToken and forward them to the AuthenticationManager.
An other interception point would be the AuthenticationManager (more precise the ProviderManager - then only real implemenation of the AuthenticationManager). It has a method Authentication authenticate(Authentication authentication) that take the user input (Subclass of Authentication for example UsernamePasswordAuthenticationToken) and verifiy it (by forwarding it to an AuthenticationProvider)
The AuthenticationProvider (for example the DaoAuthenticationProvider) would be an other place to intercept the password.
The DaoAuthenticationProvider uses a PasswordEncoder to hash the user entered password. Then the DaoAuthenticationProvider will compare the hash password obtained from the database with the hashed password entered by the user. So the PasswordEncoder is probably the easiest way to intercept the user entered password!
And of course you can intercept the HttpRequest itself: eighter you register an additional SecurityFilter (before the UsernamePasswordFilter) or a simple Servlet Filter (before the Spring Security Filter). (A Spring Interceptor will not work, because the Spring Security Filter will handle the request an will not forward it to the Spring Dispatcher, so the Spring Dispatcher can not invoke the Spring Interceptor.)
password encoder registration:
<sec:authentication-manager alias="authenticationManager">
<sec:authentication-provider user-service-ref="jdbcUserService">
<sec:password-encoder ref="myPasswordEncoder"/>
</sec:authentication-provider>
</sec:authentication-manager>
<beans:bean id="myPasswordEncoder"class="InterceptingPassordEncoderSubclassShaPasswordEncoder" />

Integrating Token based security into existing Spring Security web application

I am designing a RESTful web services that needs to be accessed by user after proper authentication. I have already developed Security for my application using Spring Security 3.0. Now I want to integrate TokenBasedAuthentication. But I stuck here for how do i do this.
My ApplicationContextSecurity.xml:
<global-method-security pre-post-annotations="enabled">
</global-method-security>
<beans:bean id="myAccessDecisionManager"
class="com.app.security.MyAccessDecisionManager">
</beans:bean>
<http auto-config="true" once-per-request="true"
access-decision-manager-ref="myAccessDecisionManager"
access-denied-page="/jsp/errorPage.jsp">
<intercept-url pattern="/*.app" access="ROLE_ANONYMOUS" />
<form-login login-page="/login.app"
login-processing-url="/j_spring_security_check" default-target-url="/login/checking.app"
authentication-failure-url="/login.app?login_error=1" />
<logout logout-url="/j_spring_security_logout"
logout-success-url="/login.app" invalidate-session="true" />
<session-management invalid-session-url="/login.app"
session-fixation-protection="newSession">
<concurrency-control max-sessions="100"
error-if-maximum-exceeded="false" />
</session-management>
</http>
<authentication-manager alias="authenticationManager">
<authentication-provider ref="customAuthenticationProvider"></authentication-provider>
</authentication-manager>
<beans:bean id="customAuthenticationProvider"
class="com.app.security.CustomAuthenticationProvider">
</beans:bean>
My CustomAuthenticationProvider :
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Autowired
private ILoginService loginService;
protected final transient Log log = LogFactory.getLog(getClass());
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
UsernamePasswordAuthenticationToken usernamePassswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
authentication.getPrincipal(), authentication.getCredentials());
// Doing authentication process here and returning authentication token
return usernamePassswordAuthenticationToken;
}
public boolean supports(Class<? extends Object> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
My requirement is,
When user want to access rest web service at first time he should provide userName/password to the server from header.
Server will accept the request, check the authentication and generate token for future requests for specific period.
Also I need client side code for how to access secured web services.
Thanks.
When user want to access rest web service at first time he should
provide userName/password to the server from header.
Server will accept the request, check the authentication and generate
token for future requests for specific period
You can do this either using HTTP headers or a normal HTTP POST request mapped to a Spring MVC controller (this is how we do it in our apps):
#Controller
public class AuthenticationController {
#Autowired
#Qualifier("authenticationManager")
AuthenticationManager authenticationManager;
#Autowired
SecurityContextRepository securityContextRepository;
#RequestMapping(method = RequestMethod.POST, value = "/authenticate")
public #ResponseBody String authenticate(#RequestParam final String username, #RequestParam final String password, final HttpServletRequest request, final HttpServletResponse response) {
final UsernamePasswordAuthenticationToken authenticationRequest = new UsernamePasswordAuthenticationToken(username, password);
final Authentication authenticationResult = this.authenticationManager.authenticate(authenticationRequest);
final String token = <some randomly generated secure token>;
final Authentication authentication = new MyAuthenticationToken(authenticationResult, token);
SecurityContextHolder.getContext().setAuthentication(authentication);
this.securityContextRepository.saveContext(SecurityContextHolder.getContext(), request, response);
return token;
}
}
Once this is done, the client should send the token in an HTTP header with every subsequent request.
Also I need client side code for how to access secured web services
Not sure what exactly you are looking for here. If your client is a JavaScript library running in a web browser, setting the authentication token as an HTTP header with every request should be straightforward. If your client is a device, the device could store the token in memory and include it as an HTTP header with every request using whatever HTTP client library you are using to invoke the services.

remember-me and authentication-success-handler

i have strange issue of for login sucess and redirect to page.
below is my spring security configuration.
<http auto-config="true" use-expressions="true">
<intercept-url pattern="/login.hst**" access="anonymous or authenticated" />
<intercept-url pattern="/**/*.hst" access="authenticated" />
<form-login login-page="/login.hst"
authentication-failure-url="/login.hst?error=true"
authentication-success-handler-ref="loginSucessHandler" />
<logout invalidate-session="true" logout-success-url="/home.hst"
logout-url="/logout.hst" />
<remember-me key="jbcpHaverERP" authentication-success-handler-ref="loginSucessHandler"/>
<session-management>
<concurrency-control max-sessions="1" />
</session-management>
</http>
LoginSuessHandler class:
#Service
public class LoginSucessHandler extends
SavedRequestAwareAuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response, Authentication authentication)
throws ServletException, IOException {
...
super.setUseReferer(true);
super.onAuthenticationSuccess(request, response, authentication);
}
}
now problem of redirect to requested page on success. if i directly refer to any secure url spring redirects me to login page and on successful login to original requested link.
but this is not working in case if user had earlier selected remember-me and then closing browser and now requesting direct URL, he is being properly authenticated but instead of redirecting him to requested page spring redirects to /. i have checked log and some spring source code and found it is not able to determine target url.
i have tried to set refer but referer value is null. but one strange thing i have noticed that in spring security configuration if i remove authentication-success-handler from remember-me configuration then it works.
<remember-me key="jbcpHaverERP" authentication-success-handler-ref="loginSucessHandler"/>
not able to figure out issue. is authentication-success-handler implementation requied to be different for form login and remember-me?
Remember-me differs from form-login in that authentication occurs during the actual request the user makes. For form-login, the user must first be redirected to the login page, submit the login form and after that they are redirected to the original target (which is usually cached in the session). So form-login requires a redirect, whereas remember-me doesn't. With a remember-me request, the user can be authenticated, and the request allowed to proceed without any intervention.
The primary purpose of an AuthenticationSuccessHandler is to control the navigation flow after authentication, so you wouldn't normally use one with remember-me at all. Using SavedRequestAwareAuthenticationSuccessHandler isn't a good idea, as there won't be a saved request available. If there is no saved request, then by default it will perform a redirect to "/" as you have observed.
If all you want is to add some functionality during a remember-me login, then you can implement the AuthenticationSuccessHandler interface directly without performing a redirect or a forward. As I explained above, you can't use the same implementation for form-login, since the current request is the submission of the login form (usually to the URL j_spring_security_check), and not a request to a URL within your application. So you need a redirect for form-login.
You would rather use ApplicationListener and look for the event InteractiveAuthenticationSuccessEvent.
InteractiveAuthenticationSuccessEvent has a property generatedBy which will be the filter, ie UsernamePasswordAuthenticationFilter (form logins) and RememberMeAuthenticationFilter (remeber me logins)
#Component
class AuthenticationApplicationListener implements ApplicationListener<InteractiveAuthenticationSuccessEvent> {
#Override
void onApplicationEvent(InteractiveAuthenticationSuccessEvent event) {
//do something
}
}
using a custom implementation of AuthenticationSuccessHandler on rememberMe will cause problems. Take a look at the flow in RememberMeAuthenticationFilter. if the successHandler is used, the filter chain is bypassed
Using an AuthenticationSuccessHandler does not work. As stated in another answer, the spring security filter chain will be bypassed!
What works, is to use an ApplicationListener - as another answer also proposes. But to find out, if your user is authenticated by remember me, the idea to use InteractiveAuthenticationSuccessEvent.getGeneratedBy() is not working: getGeneratedBy returns Class<T>, that means a generic. Therefore at runtime you cannot find out, if T is a RememberMeAuthenticationFilter.
What worked fine for me: Use InteractiveAuthenticationSuccessEvent.getAuthentication().
Here an example (by the way: #EventListener is used since Spring Security 4.2 - if you use an earlier version, do the following via implementing ApplicationListener<InteractiveAuthenticationSuccessEvent>):
#Component
public class AuthenticationApplicationListener {
#EventListener
public void handleInteractiveAuthenticationSuccess(InteractiveAuthenticationSuccessEvent event) {
if (RememberMeAuthenticationToken.class.isAssignableFrom(event.getAuthentication().getClass())) {
.... do some stuff
}
}
}
You should implement different authentication-success-handler for login form and for remember-me.
If you want to perform redirect in remeber-me handler you can use SimpleUrlAuthenticationSuccessHandler and set DefaultTargetUrl.
public class RememberMeAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
// ...
super.setAlwaysUseDefaultTargetUrl(true);
super.setDefaultTargetUrl(request.getRequestURL().toString());
super.onAuthenticationSuccess(request, response, authentication);
}

Resources