Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 363 Vote(s) - 3.57 Average
  • 1
  • 2
  • 3
  • 4
  • 5
How to secure REST API with Spring Boot and Spring Security?

#1
I know that securing REST API is widely commented topic but I'm not able to create a small prototype that meets my criteria (and I need to confirm that these criteria are realistic). There are so many options how to secure resources and how work with Spring security, I need to clarify if my needs are realistic.

**My requirements**

- Token based authenticator - users will provide its credentials and get unique and time limited access token. I would like to manage token creation, checking validity, expiration in my own implementation.
- Some REST resources will be public - no need to authenticate at all,
- Some resources will be accessible only for users with administrator rights,
- Other resource will be accessible after authorization for all users.
- I don't want to use Basic authentication
- Java code configuration (not XML)

**Current status**

My REST API works very well, but now I need to secure it. When I was looking for a solution I created a `javax.servlet.Filter` filter:

@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

HttpServletRequest request = (HttpServletRequest) req;

String accessToken = request.getHeader(AUTHORIZATION_TOKEN);
Account account = accountDao.find(accessToken);

if (account == null) {
throw new UnauthorizedException();
}

chain.doFilter(req, res);

}

But this solution with `javax.servlet.filters` doesn't work as I need because there is an issue with exception handling via `@ControllerAdvice` with Spring `servlet dispatcher`.

**What I need**

I would like to know if these criteria are realistic and get any help, how to start securing REST API with Spring Security. I read many tutorials (e.g. [Spring Data REST + Spring Security] [1]) but all work in very basic configuration - users with **their credentials are stored in memory** in configuration and I need to work with DBMS and create own authenticator.

**Please give me some ideas how to start.**

[1]:

[To see links please register here]

Reply

#2
I searched this long time too.I am working on a similar project.I found out Spring has a module to implement session via redis. It looks easy and useful.
I will add to my project too. Can be helpful:

[To see links please register here]

Reply

#3
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.

For Complete code please find the below link:

[To see links please register here]



Reply

#4
> Token based authentication - users will provide its credentials and get
> unique and time limited access token. I would like to manage token
> creation, checking validity, expiration in my own implementation.

Actually, use Filter for token Auth - best way in this case

Eventually, you can create CRUD via Spring Data for managing Token's properties like to expire, etc.

Here is my token filter:

[To see links please register here]


And Token Service Implementation

[To see links please register here]


> Some REST resources will be public - no need to authenticate at all

It's not a problem, you can manage your resources via Spring security config like this: `.antMatchers("/rest/blabla/**").permitAll()`

> Some resources will be accessible only for users with administrator rights,

Take a look at `@Secured` annotation to class. Example:

@Controller
@RequestMapping(value = "/adminservice")
@Secured("ROLE_ADMIN")
public class AdminServiceController {


> The other resource will be accessible after authorization for all users.


Back to Spring Security configure, you can configure your url like this:

http
.authorizeRequests()
.antMatchers("/openforall/**").permitAll()
.antMatchers("/alsoopen/**").permitAll()
.anyRequest().authenticated()


> I don't want to use Basic authentication


Yep, via token filter, your users will be authenticated.

> Java code configuration (not XML)

Back to the words above, look at `@EnableWebSecurity`.
Your class will be:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {}

You have to override the **configure** method. Code below, just for example, how to configure matchers. It's from another project.

@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/assets/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.usernameParameter("j_username")
.passwordParameter("j_password")
.loginPage("/login")
.defaultSuccessUrl("/", true)
.successHandler(customAuthenticationSuccessHandler)
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.invalidateHttpSession(true)
.logoutSuccessUrl("/")
.deleteCookies("JSESSIONID")
.logoutRequestMatcher(new AntPathRequestMatcher("/logout"))
.and()
.csrf();
}
Reply

#5
To validate REST API there are 2 ways

1 - Basic authentication using default username and password set up in application.properties file

[Basic Authentication][1]

2 - Authenticate using database (userDetailsService) with the actual username and password

[Advanced Authentication][2]


[1]:
[2]:
Reply

#6
## Another way that uses `http.addFilterBefore()` with custom filters
This solution is more like a skeleton to help you set up the basics.
I've created a `working demo` and added some necessary comments to help understand the process. It comes with with some simple `role-based` and `permission-based` authentication/authorization, a `publically accessable endpoint` settings that you can easily pick up and use.
##### So it's better to see the full code, and run the app in action: [github repo][1]

User class set up:
```
public class User implements UserDetails {

private final String username;
private final String password;
private final List<? extends GrantedAuthority> grantedAuthorities;

public User(
String username,
String password,
List<? extends GrantedAuthority> grantedAuthorities
) {
this.username = username;
this.password = password;
this.grantedAuthorities = grantedAuthorities;
}

// And other default method overrides
}
```
Adding custom filters through `addFilterBefore()` method:
```
http
.authorizeRequests()
.antMatchers("/")
.permitAll()
.addFilterBefore( // Filter login request only
new LoginFilter("login", authenticationManager()),
UsernamePasswordAuthenticationFilter.class
)
.addFilterBefore( // Filter logout request only
new LogoutFilter("logout"),
UsernamePasswordAuthenticationFilter.class
)
.addFilterBefore( // Verify user on every request
new AuthenticationFilter(),
UsernamePasswordAuthenticationFilter.class
);
```
Custom `LoginFilter` extends `AbstractAuthenticationProcessingFilter`, and override three methods, to deal with autentication:
```
public class LoginFilter extends AbstractAuthenticationProcessingFilter {

public LoginFilter(String url, AuthenticationManager authManager) {
super(url, authManager);
}

@Override
public Authentication attemptAuthentication(
HttpServletRequest req,
HttpServletResponse res
)
throws AuthenticationException, IOException {
LoginUserDto loginUserDto = new ObjectMapper() // this dto is a simple {username, password} object
.readValue(req.getInputStream(), LoginUserDto.class);

return getAuthenticationManager()
.authenticate(
new UsernamePasswordAuthenticationToken(
loginUserDto.getUsername(),
loginUserDto.getPassword()
)
);
}

@Override
protected void successfulAuthentication(
HttpServletRequest req,
HttpServletResponse res,
FilterChain chain,
Authentication auth
)
throws IOException, ServletException {
User user = (User) auth.getPrincipal();

req.getSession().setAttribute(UserSessionKey, user); // Simply put it in session

res.getOutputStream().print("You are logged in as " + user.getUsername());
}

@Override
protected void unsuccessfulAuthentication(
HttpServletRequest request,
HttpServletResponse response,
AuthenticationException failed
)
throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType("text/plain");
response.getOutputStream().print(failed.getMessage());
}
}
```
Custom `AuthenticationFilter` check for `auth info` stored in session and pass to `SecurityContext`:
```
public class AuthenticationFilter extends GenericFilterBean {

@Override
public void doFilter(
ServletRequest request,
ServletResponse response,
FilterChain filterChain
)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpSession session = req.getSession();

User user = (User) session.getAttribute(UserSessionKey);

if (user != null) {
UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
user,
user.getPassword(),
user.getAuthorities()
);

SecurityContextHolder.getContext().setAuthentication(authToken);
}

// Either securityContext has authToken or not, we continue the filter chain
filterChain.doFilter(request, response);
}
}
```
Custom `LogoutFilter` is rather simple and straightforward, invalidate session and terminate authentication process:
```
public class LogoutFilter extends AbstractAuthenticationProcessingFilter {

public LogoutFilter(String url) {
super(url);
}

@Override
public Authentication attemptAuthentication(
HttpServletRequest req,
HttpServletResponse res
)
throws AuthenticationException, IOException {
req.getSession().invalidate();
res.getWriter().println("You logged out!");

return null;
}
}
```
### A bit of explanation:
What these three custom filters do is that, `login` and `logout` filter only listen to their repective endpoint.

In login filter, we get `username and password` sent from client and check it against a DB(in real world) for validation, if it's valid user, then put it in session and pass it onto `SecurityContext`.

In logout filter, we simply `invalidate the session` and return a string.

While the custom `AuthenticationFilter` will authenticate every incomming request in an attempt to get user info from session, and then pass it onto `SecurityContext`.


[1]:

[To see links please register here]

Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through