Authentication by JWT and Authorization by local database - spring-security

In my Spring Boot application, my users are authenticated by a third party and I receive a Jwt which I parse using a NimbusJwtDecoder in Spring Security.
#Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.mvcMatchers("/api/private").authenticated()
.mvcMatchers("/api/organisations").authenticated()
.mvcMatchers("/api/project").authenticated()
.mvcMatchers("/api/user").authenticated()
.and()
.oauth2ResourceServer().jwt();
}
This works fine, now I want to add authorization by retrieving the roles and permissions which I would store on my own database. I have the username from the Jwt, but I am lost how to plug it all in. Any help will be greatly appreciated.

When you authenticate the user with JWT, add the username to the principal of the SecurityContext.
Create an AuthorisationFilter class that implements the javax.servelet.Filter interface. In this class get the userdetails from the database using the username from the securityContextHolder, and add the grantedAuthorities to the context.
In the SecurityConfig create a FilterRegistrationBean which sets the filter as the AuthorisationFilter, urlPatterns as ("/*") or as appropriate, and the Order as Ordered.LOWEST_PRECEDENCE

Related

SpringSecurity UserDetailsService REST Client

I'm using SpringBoot 2.4.7 and I'm trying to implement jdbc Authentication. The problem is that I can't reach the backend via http. I have read that with following configuration:
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/login")
.permitAll()
.and()
.formLogin()
....
I can reach a default login page at my context application address. But I would like to call a POST login endpoint with username and password parameters.
How can I do this?
If you are trying to receive the user credentials via a REST Endpoint and manually authenticate the user you can do this way:
#RestController
#RequestMapping("/login")
public class LoginController {
private final AuthenticationManager authenticationManager;
// constructor injecting authenticationManager
#PostMapping
public void login(#RequestBody UserCredentials credentials) {
UsernamePasswordAuthenticationToken token
= new UsernamePasswordAuthenticationToken(credentials.getUsername(), credentials.getPassword());
Authentication auth = this.authenticationManager.authenticate(token);
if (auth != null) {
SecurityContext context = SecurityContextHolder.createEmptyContext();
context.setAuthentication(auth);
SecurityContextHolder.setContext(context);
}
throw new SomeException();
}
}
This way, the Filters will take care of the rest of the authentication steps for you. The Spring Security documentation can be researched for more details.
If you want to use the endpoint generated with the default login page, you can follow the steps from the documentation to make your own request:
The form should perform a post to /login
The form will need to
include a CSRF Token which is automatically included by Thymeleaf.
The form should specify the username in a parameter named username
The form should specify the password in a parameter named password
If the HTTP parameter error is found, it indicates the user failed to
provide a valid username / password
If the HTTP parameter logout is
found, it indicates the user has logged out successfully

How to authenticate user from database using Spring rest api and oauth security

I want to implement Rest api security using oauth2 and I have implemented this security using static user data by following this. Now I would like to change this case to authenticate data from database using jdbc but till now I'm not able to find any tutorial on jdbc authentication. Please do suggest me some examples of my requirements, My requirement is Spring mvc Rest api+OAuth2+jdbc database+java-config example.
currently I have tested with static users like below.
public void globalUserDetails(AuthenticationManagerBuilder auth) throws
Exception {
auth.inMemoryAuthentication()
.withUser("crmadmin").password("crmpass").roles("ADMIN","USER").and()
.withUser("crmuser").password("pass123").roles("USER");
}
I think you can configure it as below:
Put the code below in the class that extends WebSecurityConfigurerAdapter
#Autowired
private DataSource securityDataSource; // from Bean where you have connection, jdbc driver set up for database containing login information
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.jdbcAuthentication().dataSource(securityDataSource);
}

Spring Security OAuth2 - optional login with server check

I'm working on a Web Project with different Spring Boot WebMVC Clients. Some of this Clients needs a authorization and I solved it with a Spring Security OAuth2 Server. The authentication works fine and I had no problems. Some Clients didn't need an login and they are public for all.
Technical facts: All clients use a mix between Angular, jQuery and simple JSP's. All apps use Spring Security and the public app configuration is like this:
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.antMatcher("/**")
.authorizeRequests()
.antMatchers("/**").permitAll()
.antMatchers("/fonts/**").permitAll()
.anyRequest().authenticated();
}
Now my question: I plan to build a Login (or Logout) Button in the Header of all apps. In the apps with a required authentication is that no problem. I can check the principal is null or not. But how can I solve this in public apps. The principal is ever null and the client didn't check the authentication status with the server. I had some ideas to fix it but nothing is working. The best way would be a automatic check in Spring Boot. But how can I configure this? Maybe I can check it with JavaScript, but my shots also didn't work.
Maybe it would help - two of my apps:
https://www.planyourtrip.travel (public application)
https://profile.planyourtrip.travel (memberonly application)
UPDATE: Maybe a better example
If I configure a public app like this
#Configuration
#EnableOAuth2Sso
public static class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(final HttpSecurity http) throws Exception {
http.antMatcher("/**")
.authorizeRequests()
.antMatchers("/**").permitAll()
.anyRequest().authenticated();
}
}
and the MVC Controller like this
#RequestMapping("/{([a-z]{2})}")
public ModelAndView start(final Principal principal) {
return new ModelAndView("start");
}
then is the Principal ever null. I think that is my Problem. I need a check with the OAuth Server and if i logged in is the principal set and if I'm not logged in it should be null.
If I had understood your question correctly, than you need that some URL pattern can be accessed without authentication. Than in that case you can use the following method to prevent authentication for certain URL patterns -
#Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/urlPattern");
}
permitAll() method defines that all the authenticated users can access mentioned URL pattern. So if you want some users to access some resources (URL) without authentication, than you have to use above method.

Spring Security 4 with custom authentication and local database

Spring Security 4 enables developers to write less code, but sometimes this also causes thing easily to get out of control. For example, now I am writing a login function, once the user pressed a button (login/unionauth URI), an OAuth 2.0 like authentication offered by a 3rd party would be launched, and finally the result comes back and we compare the user with our local database. In order to do so, first I have adapter class like this:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/**")
.authorizeRequests()
.antMatchers("/", "/login**", "/webjars/**").permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint("/"))
.and()
.logout()
.logoutSuccessUrl("/").permitAll()
.and()
.csrf()
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.and()
.addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class);
}
#Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(myUserDetailsService);
}
Then I have ssoFilter() like this:
public class UnionAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {
#Override
public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException {
// authentication steps.
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("tom", "N/A", null);
AuthenticationManager manager = this.getAuthenticationManager();
return manager.authenticate(token);
}
Now, the problem is, manager is null. Why it is null? I think in case of password username mode, the auth.userDetailsService(myUserDetailsService); in adapter would enable a DAO manager. In official website, it has this:
How to Add a Local User Database
Many applications need to hold data about their users locally, even if
authentication is delegated to an external provider. We don’t show the
code here, but it is easy to do in two steps.
Choose a backend for your database, and set up some repositories (e.g.
using Spring Data) for a custom User object that suits your needs and
can be populated, fully or partially, from the external
authentication.
Provision a User object for each unique user that logs in by
inspecting the repository in your /user endpoint. If there is already
a user with the identity of the current Principal, it can be updated,
otherwise created.
Hint: add a field in the User object to link to a unique identifier in
the external provider (not the user’s name, but something that is
unique to the account in the external provider).
Any idea how to add database into Spring Security OAuth2 or why the manager in the first paragraph is null?

Spring Session and Spring Security

I have questions on the following areas: spring-session and spring-security.
Spring Session
I have a application protected with Spring Security through basic in-memory authentication as provided in the example sample.
I see spring is creating session id's even the authentication is not successful, meaning I am seeing x-auth-token in my response header as well in the Redis DB even if I don't supply basic authentication credential details.
How do we avoid creating sessions for authentication failures?
Spring Security
Want to use spring security to protect resources assuming spring session creates session only for the protected resources.
Assuming a Signin API (/signin - HTTP Post) validates (username & password) credentials against a third-party REST API .
Once the external API validates the credentials, how do I update the spring security context on the successful authentication?
Access to other secured resources with the x-auth-token needs to be validated and based on the information access to the secured resource should be provided.
Do we need to have Spring Security in this case or shall I use a basic filter and spring session? What is recommended?
Typically it would be best to break your questions into multiple StackOverflow questions since you are more likely to find someone that knows the answer to a single question than both.
How do we avoid creating sessions for authentication failures ?
By default Spring Security will save the last unauthenticated request to session so that after you authenticate it can automatically make the request again. For example, in a browser if you request example.com/a/b/c and are not authenticated, it will save example.com/a/b/c to the HttpSession and then have the user authenticate. After you are authenticated, it will automatically give you the result of example.com/a/b/c. This provides a nice user experience so that your users do not need to type the URL again.
In the case of a REST service this is not necessary since the client would remember which URL needs to be re-requested. You can prevent the saving by modifying the configuration to use a NullRequestCache as shown below:
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.anyRequest().authenticated()
.and()
.requestCache()
.requestCache(new NullRequestCache())
.and()
.httpBasic();
}
You can provide custom authentication by providing your own AuthenticationProvider. For example:
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.AuthorityUtils;
public class RestAuthenticationProvider implements AuthenticationProvider {
public Authentication authenticate(Authentication authentication)
throws AuthenticationException {
UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;
String username = token.getName();
String password = (String) token.getCredentials();
// validate making REST call
boolean success = true;
// likely your REST call will return the roles for the user
String[] roles = new String[] { "ROLE_USER" };
if(!success) {
throw new BadCredentialsException("Bad credentials");
}
return new UsernamePasswordAuthenticationToken(username, null, AuthorityUtils.createAuthorityList(roles));
}
public boolean supports(Class<?> authentication) {
return (UsernamePasswordAuthenticationToken.class
.isAssignableFrom(authentication));
}
}
You can then configure your RestAuthenticationProvider using something like this:
#EnableWebSecurity
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
#Bean
public RestAuthenticationProvider restAuthenticationProvider() {
return new RestAuthenticationProvider();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth, AuthenticationProvider provider) throws Exception {
auth
.authenticationProvider(provider);
}
}
Session IDs are getting stored in Redis even when authentication fails.
Rob's answer of setting NullRequestCache didn't work for me. That is, there were redis entries even after setting request cache to NullRequestCache. To make it work, I had to use an authentication failure handler.
http.formLogin().failureHandler(authenticationFailureHandler()).permitAll();
private AuthenticationFailureHandler authenticationFailureHandler() {
return new AuthenticationFailureHandler();
}
public class AuthenticationFailureHandler
extends SimpleUrlAuthenticationFailureHandler {
}
Note that the failure handler does nothing but extend the default handler explicitly. It returns a 401 in case of failure. If you were doing redirects, you can configure it in the handler easily.

Resources