[JAVA] Implemented authentication function with Spring Security ③

** This is a continuation of Last time. ** **

** * We will create many files from here. If you make a mistake, there is a high possibility that you will not be able to go back </ font>, so it is a good idea to copy the project information up to this point or list it on Github. ** ** I didn't do this and it took me a long time to create the project many times. SpringBoot_Di_Security_DB_13.png

5. Implementation of Spring Security authentication function.

・ This is the main dish. If the implementation up to the last time is not successful, error handling will be troublesome, so let's implement it perfectly so far.

Edit 5-1, POM.

-First, rewrite pom.xml that describes Maven dependency to introduce Spring Security. We will also introduce javax.persistence to use the entity class that stores the data received from the DB.

pom.xml



<!-- SpringSecurity -->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--Thymeleaf extension (security)-->
<dependency>
	<groupId>org.thymeleaf.extras</groupId>
	<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.persistence/javax.persistence-api -->
<!--To use Entity annotation-->
<dependency>
    <groupId>javax.persistence</groupId>
    <artifactId>javax.persistence-api</artifactId>
    <version>2.2</version>
</dependency>
<!-- JpaRepository-->
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

・ ** If you just write the code, it will not be reflected. ** Maven project needs to be updated.

"Right-click the project"-> "Maven"-> "Update project" to update the Maven file. If the selected file is correct, click "OK" to update it.

-If there is no error on the console when you run the app, it's OK.

5-2, Implement the authentication function.

·It is the end. Finally, we will create a lot of files. The file structure image is as follows. ** Red frame is newly created </ font>, Yellow frame is the file to be edited </ font>. ** **

SpringBoot_Di_Security_DB_13.png

-Also, I made an image of the processing flow, so please refer to it. (I'm sorry it's very hard to see ...)

SpringBoot_Di_Security_DB_14.png

・ Put the code. I've written a lot of comments, but honestly it's hard to understand everything and it can be wrong. We hope that you will investigate and deepen your understanding.

java:SpringLogin.app.config/WebSecurityConfig.java



package SpringLogin.app.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import SpringLogin.app.service.UserDetailsServiceImpl;

/**
 *Setting class for using Spring Security
 *Set parameters in login process, data access destination in screen transition and authentication process
 * @author aoi
 *
 */
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
	
	//Instantiate the method of UserDetailsServiceImpl so that it can be used.
	@Autowired
	private UserDetailsServiceImpl userDetailsService;
	
	//Since the password obtained from the DB to be compared with the form value is encrypted, it is also used to encrypt the form value.
	@Bean
	public BCryptPasswordEncoder passwordEncoder() {
		BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
		return bCryptPasswordEncoder;
	}
	
	/**
	 *Set a request to ignore authorization settings
	 *Static resources(image,javascript,css)Exclude from authorization processing
	 */
	@Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers(
                            "/images/**",
                            "/css/**",
                            "/javascript/**"
                            );
    }
	
	/**
	 *Set authentication / authorization information
	 *Set the value of name attribute to get the URL / parameter of screen transition
	 *Overriding the Spring Security configure method.
	 */
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
		    .authorizeRequests()
		        .anyRequest().authenticated()
		        .and()
		    .formLogin()
		        .loginPage("/login") //Since the login page does not go through the controller, it is necessary to link it with ViewName.
		        .loginProcessingUrl("/login") //Submit URL of the form, authentication process is executed when a request is sent to this URL
		        .usernameParameter("username") //Explicit name attribute of request parameter
		        .passwordParameter("password")
		        .defaultSuccessUrl("/userList", true) //URL to transition to when authentication is successful
		        .failureUrl("/login?error") //URL to transition to when authentication fails
		        .permitAll() //Any user can connect.
		        .and()
		    .logout()
		        .logoutUrl("/logout")
		        .logoutSuccessUrl("/login?logout")
		        .permitAll();
	}
	
	/**
	 *Setting method that defines the data source used for authentication
	 *Here, the user information acquired from the DB is set in the userDetailsService to be the comparison information at the time of authentication.
	 * @param auth
	 * @throws Exception
	 *AuthenticationManagerBuilder has authentication functions.
	 *userDetailsService is also one of them to determine if the user entered in the form is available.
	 * https://docs.spring.io/spring-security/site/docs/4.0.x/apidocs/org/springframework/security/config/annotation/authentication/builders/AuthenticationManagerBuilder.html
	 */
	@Autowired
	public void configure(AuthenticationManagerBuilder auth) throws Exception{
		auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
		/*
		 *For in-memory, use:
		auth
		    .inMemoryAuthentication()
		        .withUser("user").password("{noop}password").roles("USER");
		*/
	}

}


java:SpringLogin.app.servise/UserDetailsServiceImpl.java



package SpringLogin.app.service;

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import SpringLogin.app.repository.LoginUserDao;
import SpringLogin.app.entity.LoginUser;

/**
 *Spring Security service implementation class for user search
 *DB can be used for authentication by specifying it as an argument of DataSource
 * @author aoi
 *
 */
@Service
public class UserDetailsServiceImpl implements UserDetailsService{
	
	//A class that implements a method to search user information from DB
	@Autowired
	private LoginUserDao userDao;
	
	/**
	 *UserDetailsService interface implementation method
	 *When the DB is searched by the user name obtained from the form and a match is found,
	 *Generate UserDetails object with password and permission information
	 *The login is judged by comparing the above input value with the password obtained from the DB in the config class.
	 */
	@Override
	public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {

		LoginUser user = userDao.findUser(userName);
		
		if (user == null) {
			throw new UsernameNotFoundException("User" + userName + "was not found in the database");
		}
		//List of permissions
		//There are Admin, User, etc., but since we will not use them this time, only USER is temporarily set.
		//When using authority, it is necessary to create and manage the authority table and user authority table on the DB.
		
		List<GrantedAuthority> grantList = new ArrayList<GrantedAuthority>();
		GrantedAuthority authority = new SimpleGrantedAuthority("USER");

		grantList.add(authority);
		
		//RawData password cannot be passed, so encryption
		BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();

        //Since UserDetails is an interface, cast the user object created by the constructor of the User class.
		UserDetails userDetails = (UserDetails)new User(user.getUserName(), encoder.encode(user.getPassword()),grantList);
		
		return userDetails;
	}

}


java:SpringLogin.app.repository/LoginUserDao.java



package SpringLogin.app.repository;

import javax.persistence.EntityManager;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import SpringLogin.app.entity.LoginUser;

/**
 *Dao calling the access method to DB
 * @author aoi
 *
 */
@Repository
public class LoginUserDao {
	
	/**
	 *An object that manages an entity.
	 *The required object because the following method casts it to the entity class LoginUser and returns the return value.
	 */
	@Autowired
	EntityManager em;
	
	/**
	 *Search for the corresponding user from the input value of the form Null is returned if there is no match
	 * @param userName
	 * @return When there is a matching user:UserEntity, when it does not exist:Null
	 */
	public LoginUser findUser(String userName) {

		String query = "";
		query += "SELECT * ";
		query += "FROM user ";
		query += "WHERE user_name = :userName "; //Use NamedParameter so that the value of the argument can be assigned with setParameter
		
		//Since the result obtained by EntityManager becomes an object, it is necessary to cast it to LoginUser type.
		return (LoginUser)em.createNativeQuery(query, LoginUser.class).setParameter("userName", userName).getSingleResult();
	}

}


java:SpringLogin.app.entity/LoginUser.java



package SpringLogin.app.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 *Entity for storing the user name and password of the login user
 * @author aoi
 *
 */
@Entity
@Table(name = "user")
public class LoginUser {
	
	@Column(name = "user_id")
	@Id
	private Long userId;
	
	@Column(name = "user_name")
	private String userName;
	
	@Column(name = "password")
	private String password;

	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

}


java:SpringLogin.app.repository/UserRepository.java



package SpringLogin.app.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import SpringLogin.app.entity.LoginUser;

/*
 *A mechanism for searching data in Spring Framework.
 *Data search becomes possible by registering in DI. The argument is <entity class,Become an ID type>
 * https://www.tuyano.com/index3?id=12626003
 */
@Repository
public interface UserRepository extends JpaRepository<LoginUser, Integer>{

}


java:SpringLogin.app.config/SpringLoginApplication.java



package SpringLogin.app.config;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@SpringBootApplication //Indicates a Spring Boot application
@ComponentScan("SpringLogin.app") //Register with DI as a bean. Can be specified as a package.
@EntityScan("SpringLogin.app.entity") //Registered in DI as a bean as above.
@EnableJpaRepositories("SpringLogin.app.repository") //For turning on Jpa Repository. Search within the specified package and@Register the class with Repository as Bean.
public class SpringLoginApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringLoginApplication.class, args);
	}

}

·thank you for your hard work. It's a long code, but it's one step further! Now that you've written the code so far, let's check it in your browser.

5-3, Operation check.

First access here → http: // localhost: 8080 / Then the URL will change to the / login screen. This is proof that Spring Security is applied, and specify the URL that can be accessed even if you are not logged in in the authorizeRequests method of WebSecurityConfig.java. However, since nothing is specified this time, you can only access the login page if you are not logged in. So when you access localhost: 8080 / you will be redirected to / login.

SpringBoot_Di_Security_DB_15.gif

・ It is OK if it works like the image above.

-Next, check the authentication function.

First, log in with the wrong path and user. Then you cannot log in & you will be redirected to "/ login? Error". This is affected by failureUrl () in WebSecurityConfig.jav.

SpringBoot_Di_Security_DB_16.gif

・ It is OK if it works like the image above.

Then log in with a legitimate user and pass. This time, two users (user1, pass1 +) (yama, kawa) have been created, but either one is OK.

SpringBoot_Di_Security_DB_17.gif

・ It is OK if it works like the image above.

Summary

That's it. Thank you for your long relationship. I hope you have somehow understood how to use the authentication function and MySQL. I haven't fully understood it yet, so if you find something wrong or missing, please comment: bow:

As an impression, honestly, Rails went up, so I licked it a little. That's because Rails had a device, so I didn't have a hard time implementing the login feature. Spring Security was an order of magnitude more difficult ... I searched around for various articles, but is there anyone who really understands this and summarizes it? I feel that. I was at a loss to post this time without understanding it, but I wanted to post it as soon as possible for people who are suffering like me, so I posted it in this state! I still have a lot to do, so I want to improve my skills! Thank you very much.

Recommended Posts