get client ip address in spring boot security - java

This is my first question here, I'm sorry if there is something wrong, please correct me).
I am working on a spring boot application with spring boot security.
I use RestController for the creation of my API.
I need to log the IP address of all clients who try to access my API, even if they are not authenticated.
I can get the client's IP after authentication or when the client accesses to the public API by this (get from HttpServletRequest):
#GetMapping("/api/public/getDeviceList")
public List<Device> getDeviceList(HttpServletRequest httpServletRequest) {
System.out.println(httpServletRequest.getRemoteHost());
}
But when the client accesses a private API with wrong credentials, I don't get these IP addresses.
How can I retrieve this information?

The method getRemoteHost() returns the name of the client system as a string.
The method getRemoteAddr() returns the client's IP address that's accessing your Java web application
Try with this on your method controller
System.out.println(httpServletRequest.getRemoteAddr());
If your server is local it will return you 0:0:0:0:0:0:0:1 but if you test on another computer on your local network, the correct ip should be shown, for example 192.168.1.4

I get solution in adding custom filter in security chain. In this filter i can log all information i need. Create filter:
public class CustomSecurityFilter extends GenericFilterBean {
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
System.out.println("Enter custom filter");
System.out.println("method:"+request.getMethod() + "\nurl:"+request.getRequestURI()+"\nip address:"+request.getRemoteAddr());
filterChain.doFilter(servletRequest, servletResponse);
}
}
then add this filter in WebSecurityConfigurerAdapter in configure(HttpSecurity http) method
#Configuration
#EnableWebSecurity
public class RestSecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.cors();
http.csrf().disable().authorizeRequests()
.antMatchers("/api/private/**").authenticated()
.and().httpBasic()
.and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
// -----Add new filter in chain-----
http.addFilterBefore(new CustomSecurityFilter(),
SecurityContextPersistenceFilter.class);
}
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/api/public/**");
}
}

Related

How to configure Spring-Data-Rest in way to only return data owned by the logged in user

I am working on a Spring Boot RESTful application which will be exposing a bunch of APIs for the web app to perform CRUD operations on the resources.
I am using spring-data-rest (along with spring-data-jpa of course) to expose the entities/repositories with the help of Spring Magic.
Even though I have secured (role-based) the endpoints with spring-security, it is not completely secure.
For example:
I have a User entity with has one-to-many relationship with Car. So the endpoint (auto exposed by spring-data-rest) for getting a user's cars is localhost:8080/users/{userId}/cars
However, any user with the required role can just pass the userId of another user and still access the endpoint.
The behavior I want is to secure these endpoints in a way that if I a logged-in user's ID is 1, then we can only hit localhost:8080/users/1/cars. Any other request with any other userId should end up in 403 or something.
Note: I know if write my own controllers then I can get a handle of the principal and do what I desire. I just want to know is there a way or pattern in spring-data-rest to achieve this?
Since you have already secured the application with Spring Security , here is another alternative with Method Security Expressions
Please review the #Pre and #Post Annotations for your requirement.
You may store the logged-in user's userId to the Authentication object.Details here.
Secure the required method with the #PreAuthorize annotation as follows
#PreAuthorize("#user.userId == authentication.principal.userId")
public List<Car> getCars(User user){..}
Do remember to enable method security
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {..}
To achieve that you need to write an Interceptor.It will be used under following situation:
Before sending the request to the controller
Before sending the response to the client
Before writing any Interceptor it should implement the HandlerInterceptor interface.
Three methods Interceptor supports are :
preHandle() method − Perform operations before sending the request
to the controller. This method should return true to return the
response to the client.
postHandle() method − Used to perform operations before sending
the response to the client.
afterCompletion() method − This is used to perform operations
after completing the request and response.
Code :
#Component
public class MyInterceptor implements HandlerInterceptor {
#Override
public boolean preHandle(
HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String pathVariablesMap = request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
//From this pathVariablesMap extract UserId and match with a loggedinUserId
}
#Override
public void postHandle(
HttpServletRequest request, HttpServletResponse response, Object handler,
) throws Exception {}
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception exception) throws Exception {}
}
By using a InterceptorRegistry you can register your Interceptors like below :
#Component
public class MyRegistoryConfig extends WebMvcConfigurer{
#Autowired
MyInterceptor myInterceptor ;
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(myInterceptor );
}
}
For more Info follow this link Interceptors
EDIT : As #Ritesh suggested added that point.
You're using spring security(great :D), so it's better to create a simple filter, register it, then simply do your custom authorize in that filter.
In brief
Create a Custom filter
Get userId from the URL path
Get userId from SecurityContextHolder (Authenticated user principal)
Compare fetched userIds
Register filter in spring security config (After BasicAuthenticationFilter)
1- Create a custom filter (Pseudo-code)
public class CustomFilter extends GenericFilterBean {
#Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException, ServletException {
//Fetch userId from path
HttpServletRequest req = (HttpServletRequest) request;
String path = req.getContextPath();
//..
//Fetch userId from SecurityContextHolder (User Principal)
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
User user = (User) authentication.getPrincipal();
Long userId = user.getId();
//Compare userId (fethced from path) with authenticated userId (fetched from principal)
//If comparison is ok
chain.doFilter(request, response);
//else
//throw Unauthorize
}
2- Register a filter in spring security config (After BasicAuthenticationFilter.class)
#Configuration
public class Configuration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterAfter(
new CustomFilter(), BasicAuthenticationFilter.class);
}
}
With this structure when an authenticated user sends a request, the request will be first checked (Comparison between userIds) and then sent.
More information for creating a filter in spring security:
A Custom Filter in the Spring Security Filter Chain

Adding different filters to different antMachers in Spring security

I'm trying to enable WebSecurity in spring. I actually have two endpoints, (one GET and one POST) and I would like to use a different GenericFilterBean for each request.
I have tried the code below, but I am not able to get the desired behaviour. With every request (GET and POST), both filters (GetUserIdByToken and AuthUserIdTransactionId) are invoked. Could you guys provide me with one solution? One and just one filter with every request. Thanks in advance.
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurity extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.requestMatchers()
.antMatchers(HttpMethod.GET, "/me/accounts/{accountId}/transactions")
.and()
.addFilterBefore(new GetUserIdByToken(), BasicAuthenticationFilter.class)
.requestMatchers()
.antMatchers(HttpMethod.POST, "/me/accounts/{accountId/transactions/{transactionId}")
.and()
.addFilterBefore(new AuthUserIdTransactionId(), BasicAuthenticationFilter.class);
}
}
Whenever you're configuring HttpSecurity, you're configuring what will end up in the FilterChain. This means that every filter you add to HttpSecurity will be invoked if the filter chain is invoked.
Method 1
If you want to selectively "turn off" certain filters in the FilterChain, you could configure them to do so:
public class GetUserIdByToken extends GenericFilterBean {
private AntPathRequestMatcher requestMatcher;
public GetUserIdByToken(String path, HttpMethod method) {
this.requestMatcher = new AntPathRequestMatcher(path, method);
}
// ...
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
if(!this.requestMatcher.matches(req) {
chain.doFilter(req,resp);
return;
}
// filter logic goes here...
}
}
You could do the same to AuthUserIdTransactionId. Then it would just be a matter of passing the appropriate paths and http methods to each constructor, and adding both filters to HttpSecurity.
Method 2
If, on the other hand, you want a completely separate FilterChain, you must configure two WebSecurityConfigurerAdapters. This blog post explains how to do it pretty well. Or you could checkout the official documentation.

Spring security auto authorization for a given IP

Is it possible in the Spring Security to (well it is Java, of course possible, so quesiton is - is it possible in some relatively painless way) automatically authorize all requests from local host (OK, some given IP) as a request that belongs to a given test user.
For instance in some filter - take all requests, check IP and if it comes from local host say something like spring.authorizeAs("user")
This answer for the similar question may help you. Based on your requirements you build principal and set it manually to Security Context.
In my case answer is following
#Component
public class LocalAuthFilter implements Filter {
#Autowired
private UserDetailsService mng;
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain filterChain) throws IOException, ServletException {
if (("127.0.0.1".equals(req.getRemoteAddr())) &&
("anonymousUser".equals(SecurityContextHolder.getContext().getAuthentication().getPrincipal()))) {
UserDetails userDetails = mng.loadUserByUsername("user"); //my test user
Authentication auth = new UsernamePasswordAuthenticationToken(
userDetails.getUsername(),
userDetails.getPassword(),
userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(auth);
}
filterChain.doFilter(req, resp);
}
#Override
public void destroy() {
}
}

Spring Boot RESTful service disable response code 401 [duplicate]

Currently, whenever a user fails authentication, spring security responds with:
{"error": "invalid_grant","error_description": "Bad credentials"}
And I would like to enhance this response with a response code like:
{"responsecode": "XYZ","error": "invalid_grant","error_description": "Bad credentials"}
After some poking around, it looks like what I need to do this is implement an AuthenticationFailureHandler, which I have begun to do. However, the onAuthenticationFailure method never seems to be reached whenever I submit invalid login credentials. I have stepped through the code, and placed logging in the onAuthenticationFailure method to confirm it is not being reached.
My failure handler is:
#Component
public class SSOAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler{
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
super.onAuthenticationFailure(request, response, exception);
response.addHeader("responsecode", "XYZ");
}
}
And my WebSecurityConfigurerAdapter contains:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired SSOAuthenticationFailureHandler authenticationFailureHandler;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.formLogin().failureHandler(authenticationFailureHandler);
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(service).passwordEncoder(passwordEncoder());
auth.authenticationEventPublisher(defaultAuthenticationEventPublisher());
}
#Bean
public DefaultAuthenticationEventPublisher defaultAuthenticationEventPublisher(){
return new DefaultAuthenticationEventPublisher();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public SSOAuthenticationFailureHandler authenticationHandlerBean() {
return new SSOAuthenticationFailureHandler();
}
#Bean
public PasswordEncoder passwordEncoder(){
PasswordEncoder encoder = new BCryptPasswordEncoder();
return encoder;
}
}
My questions are:
Is this the right way to achieve the result I want? (Customizing the spring security authentication response)
If so, did I do something wrong trying to set up my authentication failure handler (since a bad login doesn't seem to be reaching the onAuthenticationFailure method?
Thank you!
You can add exception handling to you Spring Security by calling .exceptionHandling() on your HttpSecurity object in your configure method.
If you only want to handle just bad credentials you can ignore the .accessDeniedHandler(accessDeniedHandler()).
The access denied handler handles situations where you hav secured you app at method level such as using the #PreAuthorized, #PostAuthorized, & #Secured.
An example of your security config could be like this
SecurityConfig.java
/*
The following two are the classes we're going to create later on.
You can autowire them into your Security Configuration class.
*/
#Autowired
private CustomAuthenticationEntryPoint unauthorizedHandler;
#Autowired
private CustomAccessDeniedHandler accessDeniedHandler;
/*
Adds exception handling to you HttpSecurity config object.
*/
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf()
.disable()
.exceptionHandling()
.authencationEntryPoint(unauthorizedHandler) // handles bad credentials
.accessDeniedHandler(accessDeniedHandler); // You're using the autowired members above.
http.formLogin().failureHandler(authenticationFailureHandler);
}
/*
This will be used to create the json we'll send back to the client from
the CustomAuthenticationEntryPoint class.
*/
#Bean
public Jackson2JsonObjectMapper jackson2JsonObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
return new Jackson2JsonObjectMapper(mapper);
}
CustomAuthenticationEntryPoint.java
You can create this in its own separate file. This is Entry point handles the invalid credentials.
Inside the method we'll have to create and write our own JSON to the HttpServletResponse object. We'll
use the Jackson object mapper bean we created in the Security Config.
#Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {
private static final long serialVersionUID = -8970718410437077606L;
#Autowired // the Jackson object mapper bean we created in the config
private Jackson2JsonObjectMapper jackson2JsonObjectMapper;
#Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException e) throws IOException {
/*
This is a pojo you can create to hold the repsonse code, error, and description.
You can create a POJO to hold whatever information you want to send back.
*/
CustomError error = new CustomError(HttpStatus.FORBIDDEN, error, description);
/*
Here we're going to creat a json strong from the CustomError object we just created.
We set the media type, encoding, and then get the write from the response object and write
our json string to the response.
*/
try {
String json = jackson2JsonObjectMapper.toJson(error);
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
response.getWriter().write(json);
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
CustomAccessDeniedHandler.java
This handles authorization errors such as trying to access method without the
appropriate priviledges. You can implement it in the same way we did above with the bad credentials exception.
#Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
#Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException e) throws IOException, ServletException {
// You can create your own repsonse here to handle method level access denied reponses..
// Follow similar method to the bad credentials handler above.
}
}
Hopefully this is somewhat helpful.

How to rewrite URLs according to DB records in Java EE

I am developing a JAX-RS multi-domain application. This application is supposed to be addressed by different domain names and shall handle these domains differently. Each domain is a record in DB and maps the domain name to UUID. Records of all domains are set to the same server IP. UUIDs are used internally as a path parameter (for example: https://{IP}/{uuid}).
I would like to achieve the following state:
https://domain1.com/{someResource} --> https://{serverIP}/domain/123e4567-e89b-12d3-a456-426655440000/{someResouce}
The illustration case:
The user accesses the resource on the URL https://my-domain.com/rest/v1/details and the server serves the details about the current domain (and similarly in other requests).
My idea was to implement a ContainerRequestFilter which would add information about requested URL (and hostname) like it works in authentication filters. The second approach was to use the Ocpsoft Rewrite library but I failed in setting it up and the documentation is not very rich in this topic.
Hi achieved it by using javax.servlet.Filter
Check it out:
#ApplicationScoped
#WebFilter(filterName = "MyAwesomeFilter", urlPatterns = {"/*"})
public class MyAwesomeFilter implements Filter {
#Override
public void init(FilterConfig filterConfig) throws ServletException {
}
#Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
final String s = request.getRequestURI() + "/addedSomething"
request.getRequestDispatcher(s).forward(request, servletResponse);
}
#Override
public void destroy() {
}
}

Categories