avatarRyan Deschamps

Free AI web copilot to create summaries, insights and extended knowledge, download it at here

5261

Abstract

e driver and (hopefully) working configuration information. Now its time to set up our database for Spring security. In Spring Security, we will need to create a UserDetailsManager that will allow us to review who has permission to what. In the beginner tutorial, you did this by using a <code>User.withDefaultPasswordEncoder()</code> call that included usernames, password and roles. In our case, we need to ask Spring to pull that information up from a database. For this, we will use the <code>JdbcUserDetailsManager()</code> call. This class extends another one called <a href="https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/core/userdetails/jdbc/JdbcDaoImpl.html">JdbcDaoImpl() whose documentation informs us of how we should set up our database</a> to accommodate Spring Security. It says we need two tables:</p><ul><li><i>Users</i> : with columns for username, enabled and password.</li><li><i>Authorities:</i> with columns for a username and an authority.</li></ul><p id="27b5">That seems pretty easy, but there are some catches! Authorities will need to accommodate a many to many relationship. There are multiple ways to do this. I am going to do it the hard way because it’s the most illustrative.</p><div id="c64c"><pre><span class="hljs-meta">@Entity</span> <span class="hljs-meta">@Table(name="users")</span> <span class="hljs-comment">// Using Lombok as an alternative to filling out getters and setters</span> <span class="hljs-meta">@Getter</span> <span class="hljs-meta">@Setter</span> <span class="hljs-meta">@NoArgsConstructor</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">User</span> { <span class="hljs-meta">@Id</span> <span class="hljs-keyword">private</span> String username; <span class="hljs-keyword">private</span> String password; <span class="hljs-comment">// Need to specify that we want MariaDB's TinyInt(1) approach to Bools.</span> <span class="hljs-meta">@Column(nullable = false, columnDefinition = "BOOLEAN")</span> <span class="hljs-keyword">private</span> Boolean enabled; }</pre></div><p id="76e2">Easy enough user table. How about authorities? You could use <code>@OneToMany</code> entry in the Users class, but let’s just build that out so we can understand things better — especially how to build a composite class in Spring JPA.</p><div id="547b"><pre><span class="hljs-meta">@Entity</span> <span class="hljs-meta">@Table(name = "authorities")</span> <span class="hljs-meta">@Getter</span> <span class="hljs-meta">@Setter</span> <span class="hljs-meta">@NoArgsConstructor</span> <span class="hljs-meta">@IdClass(AuthorityId.class)</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Authorities</span> { <span class="hljs-meta">@Id</span> String username; <span class="hljs-meta">@Id</span> String authority; }</pre></div><p id="aff9">Unfortunately, this is not enough to get us through our day. You will notice that the IdClass is saying we have another class that will guide us towards what we will need for a composite class. Let’s build that now.</p><div id="55a9"><pre><span class="hljs-meta">@EqualsAndHashCode</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">AuthorityId</span> { <span class="hljs-keyword">private</span> <span class="hljs-title class_">String</span> username; <span class="hljs-keyword">private</span> <span class="hljs-title class_">String</span> authority;

<span class="hljs-keyword">public</span> <span class="hljs-title class_">AuthorityId</span>(<span class="hljs-title class_">String</span> username, <span class="hljs-title class_">String</span> authority) { <span class="hljs-variable language_">this</span>.<span class="hljs-property">username</span> = username; <span class="hljs-variable language_">this</span>.<span class="hljs-property">authority</span> = authority; } }</pre></div><p id="f379">Notice that we need the <code>@EqualsAndHashCode</code> implementations so we can use this class as a combined identifier class. When you run the project, good old Spring JPA will create tables for you. Now we want to add our first user. While this is good enough for now, you may also want to connect this user identity to another class so you can get other important information like email address, full name, an avatar and so on. For this, you could just do the following:</p><div id="9da0"><pre><span class="hljs-meta">@Entity</span> <span class="hljs-comment">// Might as well add an all arguments constructor and a builder</span> <span class="hljs-comment">// so you can build a profile user on the fly</span> <span class="hljs-meta">@Setter</span> <span class="hljs-meta">@Getter</span> <span class="hljs-meta">@NoArgsConstructor</span> <span class="hljs-meta">@AllArgsConstructor</span> <span class="hljs-meta">@Builder</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">UserProfile</span> { <span class="hljs-meta">@Id</span> <span class="hljs-meta">@GeneratedValue(strategy=GenerationType.AUTO)</span> <spa

Options

n class="hljs-keyword">private</span> <span class="hljs-type">int</span> id; <span class="hljs-meta">@OneToOne</span> <span class="hljs-keyword">private</span> User user; <span class="hljs-keyword">private</span> String firstName <span class="hljs-comment">// last name, email, etc.</span> }</pre></div><p id="66d3">Now we can go back to our Security Configuration tutorial.</p><div id="daa8"><pre><span class="hljs-meta">@Configuration</span> <span class="hljs-comment">// you could decide to include your properties in another spot</span> <span class="hljs-meta">@PropertySource</span>(<span class="hljs-string">"classpath:application.properties"</span>) <span class="hljs-meta">@EnableWebSecurity</span> <span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title class_">Security</span> <span class="hljs-title class_">Config</span> <span class="hljs-keyword">extends</span> <span class="hljs-title class_">WebSecurityConfigurerAdapter</span> { <span class="hljs-comment">// you could extend to use VaadinWebSecurity </span> <span class="hljs-comment">// if you wish</span>

<span class="hljs-comment">// get database information from Spring JPA</span> <span class="hljs-meta">@Autowired</span> <span class="hljs-keyword">private</span> <span class="hljs-title class_">DataSource</span> dataSource;

<span class="hljs-comment">// use Spring's Value annotation to get data from application.properties</span> <span class="hljs-meta">@Value</span>(<span class="hljs-string">"{my-project.default-password}"</span>) <span class="hljs-keyword">private</span> <span class="hljs-title class_">String</span> default_password; <span class="hljs-meta">@Value</span>(<span class="hljs-string">"{my-project.default-username}"</span>) <span class="hljs-keyword">private</span> <span class="hljs-title class_">String</span> default_username;

<span class="hljs-meta">@Override</span> <span class="hljs-keyword">protected</span> <span class="hljs-built_in">void</span> <span class="hljs-title function_">configure</span>(<span class="hljs-params">HttpSecurity http</span>){ http.<span class="hljs-title function_">authorizeHttpRequests</span>() .<span class="hljs-title function_">requestMatchers</span>(<span class="hljs-keyword">new</span> <span class="hljs-title class_">AntPathRequestMatcher</span>(<span class="hljs-string">"/"</span>)).<span class="hljs-title function_">permitAll</span>();

<span class="hljs-variable language_">super</span>.<span class="hljs-title function_">configure</span>(http);
<span class="hljs-comment">// if you decide to use Vaadin.</span>
<span class="hljs-comment">// setLoginView(http, LoginView.class);</span>

}

<span class="hljs-meta">@Bean</span> <span class="hljs-keyword">public</span> <span class="hljs-title class_">PasswordEncoder</span> <span class="hljs-title function_">passwordEncoder</span>(<span class="hljs-params"></span>) { <span class="hljs-comment">// for encoding our passwords.</span> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-title class_">BCryptPasswordEncoder</span>(); }

<span class="hljs-meta">@Bean</span> <span class="hljs-keyword">public</span> <span class="hljs-title class_">UserDetailsService</span> <span class="hljs-title function_">userDetailsService</span>(<span class="hljs-params">PasswordEncoder passwordEncoder</span>) { <span class="hljs-comment">// This is all we really need to do.</span> <span class="hljs-title class_">JdbcUserDetailsManager</span> dbManager = <span class="hljs-keyword">new</span> <span class="hljs-title class_">JdbcUserDetailsManager</span>(dataSource);

<span class="hljs-comment">// But we would like to have a default super-user, so let's create one:</span>

dbManager.<span class="hljs-title function_">createUser</span>(
  <span class="hljs-title class_">User</span>
    .<span class="hljs-title function_">withUsername</span>(default_username)
    .<span class="hljs-title function_">password</span>(<span class="hljs-title function_">passwordEncoder</span>().<span class="hljs-title function_">encode</span>(default_password)
  <span class="hljs-comment">// give all the powers!</span>
    .<span class="hljs-title function_">roles</span>(<span class="hljs-string">"USER"</span>, <span class="hljs-string">"ADMIN"</span>, <span class="hljs-string">"SUPER_ADMIN"</span>)
    .<span class="hljs-title function_">build</span>()
 );
 <span class="hljs-keyword">return</span> dbManager;

}

} </pre></div><p id="72b1">Once you start your project, you should be able to see your new user inside your database. Spring security will also allow you to login with all the associated permissions. You may want to explore <a href="https://vaadin.com/docs/latest/security/enabling-security">VaadinWebSecurity</a> to show how to allow different users with different roles access different kinds of content.</p><p id="a3b1">That’s all there is to it! Hopefully there is enough here to help you move forward on the more exciting business logic things you hope to do with your newly secured web application.</p></article></body>

User Authentication with Spring Security and MariaDB

People learning Spring for the first time will find that certain tutorials will only get them so far. Spring Security is one of those tutorials. For example, most tutorials will show how to set up a basic security configuration with a deeply problematic `InMemoryUserDetailsService` with hard-coded usernames, passwords and authorities or roles. This approach also comes with a deprecation warning that will remind developers that it is insecure to use this approach in production. Okay fine. But why are we building secure websites that are not secure in production? That seems futile. What do we do when our bosses actually want us to do good work?

Database-ready Spring Boot Security

This tutorial is going to assume you know a few things about Spring:

  • That you understand how to set up a project using the Spring Initializr or via an IDE like VS Code or Eclipse.
  • That you know the basic of Spring Boot Application Design including how to set up a Controller and Spring Boot Application.
  • You have reasonable in-depth knowledge of a Spring UI approach such as with Thymeleaf or Vaadin.
  • Familiarity with a data source of your choice (we will use MariaDB as the example in this tutorial).
  • Removing boilerplate java code via Lombok.
  • In sum, this is not a beginner tutorial. We are going to look at production-ready Spring.

With that in mind, I recommend including the following dependencies to your project:

  • Spring Boot Security
  • Spring Data Java Persistence
  • Spring boot starter web
  • Spring boot dev tools
  • A database driver (we will use MariaDB)

The first thing we need to do is set up our database. In addition, I would like to assume that we will want to include a default username and password for our project in our `application.properties` file. We find this file under `/my_project/src/main/resources` in a typical package. Note that your spring project will not work if no database exists at the url localhost:3306/my_db_name.

spring.datasource.url=jdbc:mariadb://localhost:3306/my_db_name
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
# This setting means that our project will drop all the tables
# every time we run. Great for prototyping.
spring.jpa.hibername.ddl-auto=create-drop
# We have metadata to support these in another file.
my-project.default-username=default-username
my-project.default-password=default-password

In addition to the application.properties settings, we also want to let Spring and anyone using our project to know that we made-up some project settings. We do this in a metadata file under /myproject/src/main/resources/META-INF/additional-spring-configuration-metadata.json. It looks like this:

{"properties": [{
  "name": "my-project.default-password",
  "type": "java.lang.String",
  "description": "project default password"},
{ "name" : "my-project.default-username",
  "type" : "java.lang.String",
  "description": "project default username"}]}

Now that our project is set up with JPA, a database driver and (hopefully) working configuration information. Now its time to set up our database for Spring security. In Spring Security, we will need to create a UserDetailsManager that will allow us to review who has permission to what. In the beginner tutorial, you did this by using a User.withDefaultPasswordEncoder() call that included usernames, password and roles. In our case, we need to ask Spring to pull that information up from a database. For this, we will use the JdbcUserDetailsManager() call. This class extends another one called JdbcDaoImpl() whose documentation informs us of how we should set up our database to accommodate Spring Security. It says we need two tables:

  • Users : with columns for username, enabled and password.
  • Authorities: with columns for a username and an authority.

That seems pretty easy, but there are some catches! Authorities will need to accommodate a many to many relationship. There are multiple ways to do this. I am going to do it the hard way because it’s the most illustrative.

@Entity
@Table(name="users")
// Using Lombok as an alternative to filling out getters and setters
@Getter @Setter @NoArgsConstructor
public class User {
  @Id
  private String username;
  private String password;
  // Need to specify that we want MariaDB's TinyInt(1) approach to Bools.
  @Column(nullable = false, columnDefinition = "BOOLEAN")
  private Boolean enabled;
}

Easy enough user table. How about authorities? You could use @OneToMany entry in the Users class, but let’s just build that out so we can understand things better — especially how to build a composite class in Spring JPA.

@Entity
@Table(name = "authorities")
@Getter @Setter @NoArgsConstructor
@IdClass(AuthorityId.class)
public class Authorities {
  @Id
  String username;
  @Id
  String authority;
}

Unfortunately, this is not enough to get us through our day. You will notice that the IdClass is saying we have another class that will guide us towards what we will need for a composite class. Let’s build that now.

@EqualsAndHashCode
public class AuthorityId {
  private String username;
  private String authority;

  public AuthorityId(String username, String authority) {
    this.username = username;
    this.authority = authority;
  }
}

Notice that we need the @EqualsAndHashCode implementations so we can use this class as a combined identifier class. When you run the project, good old Spring JPA will create tables for you. Now we want to add our first user. While this is good enough for now, you may also want to connect this user identity to another class so you can get other important information like email address, full name, an avatar and so on. For this, you could just do the following:

@Entity
// Might as well add an all arguments constructor and a builder
// so you can build a profile user on the fly
@Setter @Getter @NoArgsConstructor @AllArgsConstructor @Builder
public class UserProfile {
  @Id
  @GeneratedValue(strategy=GenerationType.AUTO)
  private int id;
  @OneToOne
  private User user;
  private String firstName
  // last name, email, etc.
}

Now we can go back to our Security Configuration tutorial.

@Configuration
// you could decide to include your properties in another spot
@PropertySource("classpath:application.properties")
@EnableWebSecurity
public class Security Config extends WebSecurityConfigurerAdapter { // you could extend to use VaadinWebSecurity 
                               // if you wish

  // get database information from Spring JPA
  @Autowired
  private DataSource dataSource;

  // use Spring's Value annotation to get data from application.properties
  @Value("${my-project.default-password}")
  private String default_password;
  @Value("${my-project.default-username}")
  private String default_username;

  @Override
  protected void configure(HttpSecurity http){
    http.authorizeHttpRequests()
      .requestMatchers(new AntPathRequestMatcher("/")).permitAll();

    super.configure(http);
    // if you decide to use Vaadin.
    // setLoginView(http, LoginView.class);
  }

  @Bean
  public PasswordEncoder passwordEncoder() {
    // for encoding our passwords.
    return new BCryptPasswordEncoder();
  }

  @Bean
  public UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) {
    // This is all we really need to do.
    JdbcUserDetailsManager dbManager = new JdbcUserDetailsManager(dataSource);

    // But we would like to have a default super-user, so let's create one:

    dbManager.createUser(
      User
        .withUsername(default_username)
        .password(passwordEncoder().encode(default_password)
      // give all the powers!
        .roles("USER", "ADMIN", "SUPER_ADMIN")
        .build()
     );
     return dbManager;
  }

} 

Once you start your project, you should be able to see your new user inside your database. Spring security will also allow you to login with all the associated permissions. You may want to explore VaadinWebSecurity to show how to allow different users with different roles access different kinds of content.

That’s all there is to it! Hopefully there is enough here to help you move forward on the more exciting business logic things you hope to do with your newly secured web application.

Spring Boot
Security
Programming
Java
Authentication
Recommended from ReadMedium