[JAVA] Nouvel enregistrement et connexion Spring-Security (JPA)

J'étais accro aux paramètres de sécurité de Spring

Comme le dit le titre. J'ai essayé d'utiliser Spring Security pour la première fois pour effectuer l'authentification, mais j'y étais accro. Les URL de référence sont ici et ici.

Commencez par créer un projet. L'environnement est le suivant. Version Java: 1.8 FW:Springboot(gradle) DB: MySQL (l'accès est JPA) Autres: Web, lombok, Thymeleaf, etc.

Tout d'abord, affichez l'écran de connexion

LoginController


package com.example.demo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;

@RestController
@RequestMapping(value="/login")
public class LoginController {
	@GetMapping
	public ModelAndView login(ModelAndView mav) {
        return mav;
    }
}

login.html


<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:th="https://www.thymeleaf.org"
	xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>S'identifier</title>
</head>
<body>
	<div class="login-form">
		<form th:action="@{/login}" th:method="post">
			<input type="text" name="loginId" id="loginId"><br />
			<input type="password" name="password" id="password"><br />
			<button type="submit" class="login-btn">S'identifier</button>
		</form>
	</div>

	<button onclick="location.href='./signup'" class="signup-link">s'inscrire</button>
</body>
</html>

Type de fichier de paramètres de sécurité Spring

WebSecurityConfig


package com.example.demo.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 com.example.demo.service.UserDetailsServiceImpl;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsServiceImpl userDetailsService;

    //Cryptage du mot de passe
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
    	return new BCryptPasswordEncoder();
    }

    //Dossiers et fichiers non filtrés
    @Override
    public void configure(WebSecurity web) throws Exception {
    	web.ignoring().antMatchers(
    			"/image/**",
    			"/css/**",
    			"/js/**"
    			);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

    	//Tout le monde peut passer de l'écran de connexion au nouvel écran d'enregistrement
    	http.authorizeRequests()
    	.antMatchers("/login", "/signup")
		.permitAll()
    	.anyRequest().authenticated();

    	//S'identifier
    	http.formLogin()
    	.loginPage("/login") //Étant donné que la page de connexion ne passe pas par le contrôleur, il est nécessaire de la lier à ViewName.
    	.loginProcessingUrl("/login") //Soumettre l'URL du formulaire, le processus d'authentification est exécuté lorsqu'une demande est envoyée à cette URL
    	.usernameParameter("loginId") //Attribut de nom explicite du paramètre de demande
    	.passwordParameter("password")
    	.successForwardUrl("/top")
    	.failureUrl("/login?error")
    	.permitAll();

    	//Se déconnecter
    	http.logout()
    	.logoutUrl("/logout")
    	.logoutSuccessUrl("/login")
    	.invalidateHttpSession(true)
    	.deleteCookies("JSESSIONID", "SESSION", "remember-me")
    	.permitAll();
    }

    @Autowired
    public void configure(AuthenticationManagerBuilder auth) throws Exception{
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }
}

Enregistrer l'utilisateur dans la base de données

Créez un compte approprié dans la base de données.

SignUpController


package com.example.demo.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;

import com.example.demo.Conversion;
import com.example.demo.form.SignUpForm;
import com.example.demo.service.SignUpService;

@RestController
@RequestMapping(value="/signup")
public class SignUpController {

	@GetMapping
	public ModelAndView signup(ModelAndView mav) {
        return mav;
    }

	@Autowired
	private SignUpService signupService;
	@Autowired
	private Conversion conversion;

	@PostMapping
	public ModelAndView signup(SignUpForm signupForm, ModelAndView mav) {
		signupService.registUser(conversion.signupCon(signupForm));
		mav.setViewName("login");
		return mav;
	}
}

SignUpForm


package com.example.demo.form;

import javax.persistence.Column;
import javax.persistence.Id;

import lombok.Data;

@Data
public class SignUpForm {

	@Id
	private Integer id;
	@Column(name = "login_id")
	private String loginId;
	private String password;
}

signup.html


<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
	xmlns:th="https://www.thymeleaf.org"
	xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>s'inscrire</title>
</head>
<body>
	<div class="signup-form">
		<form th:action="@{/signup}" th:method="post" th:object="${SignUpForm}">
			<label class="loginId-label">Identifiant de connexion</label>
			<input type="text" name="loginId" id="loginId"><br />
			<label class="password-label">mot de passe</label>
			<input type="password" name="password" id="password"><br />
			<button type="submit" class="signup-btn">enregistrement</button>
		</form>
	</div>
</body>
</html>

UserEntity


package com.example.demo.entity;

import java.util.ArrayList;
import java.util.Collection;

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

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import lombok.Data;

@Data
@Table(name="user")
@Entity
public class UserEntity{
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Integer id;
	@Column(name = "login_id")
	private String loginId;
	private String password;

	public UserEntity(Integer id, String loginId, String password) {
		this.id = id;
		this.loginId = loginId;
		this.password = password;
	}
}

Cryptez simplement le mot de passe reçu dans Form et transmettez-le à Entity. J'ai essayé de le faire uniquement avec Entity, mais je n'ai pas bien compris, alors dites-moi qui le comprend.

Conversion


package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;

import com.example.demo.entity.UserEntity;
import com.example.demo.form.SignUpForm;

@Component
public class Conversion {
	@Autowired
	BCryptPasswordEncoder passwordEncoder;

	public UserEntity signupCon(SignUpForm form) {
		return new UserEntity(
				form.getId(),
				form.getLoginId(),
				passwordEncoder.encode(form.getPassword()));
	}
}

Après cela, laissez Service enregistrer la valeur de Entity avec JPA.

Point addictif ①

En premier lieu, c'était la première fois que j'utilisais Thymeleaf ou JPA, donc je suis tombé sur diverses choses, mais pour le moment, j'ai réussi à l'obtenir. Supprimez également la page d'inscription de Sécurité.

Fonction de connexion

L'authentification de connexion se fait enfin à partir d'ici. Paramètres de sécurité Spring. Il semble que loadUserByUsername de UserDetailsService sert à effectuer le processus d'authentification lui-même. Mais cet argument est `` String username '', donc le mot de passe n'est-il pas déterminé ici? Je ne sais pas comment faire.

UserDetailsServiceImpl


package com.example.demo.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.stereotype.Service;

import com.example.demo.entity.UserEntity;
import com.example.demo.repository.UserRepository;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

	@Autowired
	private UserRepository userRepository;

	//loadUserByUsername est une méthode de UserDetails
	@Override
	public UserDetails loadUserByUsername(String loginId) throws UsernameNotFoundException {

		UserEntity loginUser = userRepository.findUser(loginId);

		if (loginUser == null) {
			throw new UsernameNotFoundException("User" + loginId + "Le compte n'existe pas");
		}
//Paramètre d'autorisation (factice)
		List<GrantedAuthority> grantList = new ArrayList<GrantedAuthority>();
		GrantedAuthority authority = new SimpleGrantedAuthority("USER");
		grantList.add(authority);

//Puisque UserDetails est une interface, transtypez l'objet utilisateur créé par le constructeur de la classe User.
		UserDetails userDetails = (UserDetails) new User(loginUser.getLoginId(), loginUser.getPassword(), grantList);

		return userDetails;
	}
}

Cette fois, j'utilise LoginController. Il semble que vous puissiez le faire avec MvcConfig, mais y a-t-il un mérite?

Après cela, ajoutez Query au référentiel de JPA et recherchez l'utilisateur avec login_id pour vous connecter! (Parce qu'il n'existait pas de recherche d'ID de chaîne dans JPA. Si quelqu'un le sait, merci de me le faire savoir)

J'ai pensé ...

Point addictif ②

Même si j'entre l'ID et le mot de passe et que j'appuie sur le bouton de connexion, l'écran supérieur n'apparaît pas. Le mappage au moment du succès (connexion réussie) est-il incorrect? Après avoir vérifié diverses choses avec, j'ai changé la partie .successForwardUrl ("/ top") de WebSecurityConfig en .defaultSuccessUrl ("/ top") et cela a changé. Je n'étais pas sûr de la différence.

Supprimer le paramètre d'autorisation (ROLE)

Je l'ai fait en référence au lien, mais lorsque j'ai créé la base de données, je n'ai pas utilisé ROLE, donc je n'ai pas besoin de privilèges d'administrateur pour le moment, non? J'étais accro parce que je ne pouvais pas du tout le faire alors que je pensais que je n'aurais pas de privilèges d'administrateur. Après tout, commentez simplement la partie d'autorité factice de UserDetailsServiceImpl et renvoyez les informations sur l'entité.

UserDetailsServiceImpl


package com.example.demo.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import com.example.demo.entity.UserEntity;
import com.example.demo.repository.UserRepository;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

	@Autowired
	private UserRepository userRepository;

	//loadUserByUsername est une méthode de UserDetails
	@Override
	public UserDetails loadUserByUsername(String loginId) throws UsernameNotFoundException {

		UserEntity loginUser = userRepository.findUser(loginId);

		if (loginUser == null) {
			throw new UsernameNotFoundException("User" + loginId + "Le compte n'existe pas");
		}
//Paramètre d'autorisation (factice)
//		List<GrantedAuthority> grantList = new ArrayList<GrantedAuthority>();
//		GrantedAuthority authority = new SimpleGrantedAuthority("USER");
//		grantList.add(authority);

//Puisque UserDetails est une interface, transtypez l'objet utilisateur créé par le constructeur de la classe User.
//		UserDetails userDetails = (UserDetails) new User(loginUser.getLoginId(), loginUser.getPassword(), grantList);

		return loginUser;
	}
}

J'ai toujours pu me connecter. Bien sûr, il devrait y avoir un administrateur, alors je me demande si l'autorité sera effectivement définie. Vous pouvez également appliquer un filtre d'autorité administrateur pour les transitions d'écran.

Résumé

Thymeleaf, JPA, Spring Security et Spring lui-même n'étaient pas vraiment compris au départ, il a donc fallu beaucoup de temps pour y arriver. Ce n'est peut-être pas très courant de commencer à partir d'ici dans le domaine réel, mais je pensais simplement le comprendre.

Recommended Posts

Nouvel enregistrement et connexion Spring-Security (JPA)
[Rails] Méthode de désinfection des paramètres forte requise lors de la personnalisation des fonctions "nouvel enregistrement", "connexion" et "mise à jour des informations"
Ancienne et nouvelle confrontation de vitesse