Vérification de la connexion à la séparation avant et arrière de SpringBoot Security

Vérification de la connexion à la séparation avant et arrière de SpringBoot Security

Cet article est reproduit. Très bien écrit, facile à comprendre

 

Récemment, j'ai fait des recherches sur l'utilisation d'OAuth2, en pensant à l'authentification unique dans son ensemble, j'ai trouvé beaucoup de démos sur Internet mais je n'ai pas réussi à les mettre en œuvre, peut-être parce que je ne comprends pas du tout le principe d'OAuth2. Pendant quelques jours, j'étais de plus en plus désemparé et je ne pouvais pas abandonner. J'ai tourné la tête et j'ai pensé, OAuth2 est une extension basée sur la sécurité, et je n'y connais rien en sécurité, alors parlons-en. étudiez d'abord la sécurité. Développez la sécurité et découvrez ce que vous ressentez.

Faites-le simplement et utilisez la sécurité dans l'environnement SpringBoot 2.1.4.RELEASE prêt à l'emploi.
Sans parler de l'utilisation d'une sécurité simple. Le projet actuel est séparé du front-end et du back-end. Le format des données renvoyées après une connexion réussie ou un échec doit être au format JSON. Il doit également renvoyer un message d'invite au format JSON lorsqu'il n'est pas connecté. Il doit également renvoyer JSON lors de la déconnexion des données formatées. Quelle que soit l'autorisation, renvoyez d'abord les données au format JSON. Celle-ci est terminée. Je l'ai également étudiée pendant plusieurs jours et lu beaucoup d'expériences d'autres personnes. Faites des recherches.

Ci-dessous, le code :

La première étape consiste à introduire le fichier de configuration de sécurité dans pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>     
    <artifactId>spring-boot-starter-security</artifactId>       
</dependency>

La deuxième étape consiste à augmenter le fichier de configuration Configuration

import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * 参考网址:
 * https://blog.csdn.net/XlxfyzsFdblj/article/details/82083443
 * https://blog.csdn.net/lizc_lizc/article/details/84059004
 * https://blog.csdn.net/XlxfyzsFdblj/article/details/82084183
 * https://blog.csdn.net/weixin_36451151/article/details/83868891
 * 查找了很多文件,有用的还有有的,感谢他们的辛勤付出
 * Security配置文件,项目启动时就加载了
 * @author 程就人生
 *
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {      
    
    @Autowired
    private MyPasswordEncoder myPasswordEncoder;
    
    @Autowired
    private UserDetailsService myCustomUserService;
    
    @Autowired
    private ObjectMapper objectMapper;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
    
        http
        .authenticationProvider(authenticationProvider())
        .httpBasic()
        //未登录时,进行json格式的提示,很喜欢这种写法,不用单独写一个又一个的类
            .authenticationEntryPoint((request,response,authException) -> {
                response.setContentType("application/json;charset=utf-8");
                response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                PrintWriter out = response.getWriter();
                Map<String,Object> map = new HashMap<String,Object>();
                map.put("code",403);
                map.put("message","未登录");
                out.write(objectMapper.writeValueAsString(map));
                out.flush();
                out.close();
            })
            
            .and()
            .authorizeRequests()
            .anyRequest().authenticated() //必须授权才能范围
            
            .and()
            .formLogin() //使用自带的登录
            .permitAll()
            //登录失败,返回json
            .failureHandler((request,response,ex) -> {
                response.setContentType("application/json;charset=utf-8");
                response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                PrintWriter out = response.getWriter();
                Map<String,Object> map = new HashMap<String,Object>();
                map.put("code",401);
                if (ex instanceof UsernameNotFoundException || ex instanceof BadCredentialsException) {
                    map.put("message","用户名或密码错误");
                } else if (ex instanceof DisabledException) {
                    map.put("message","账户被禁用");
                } else {
                    map.put("message","登录失败!");
                }
                out.write(objectMapper.writeValueAsString(map));
                out.flush();
                out.close();
            })
            //登录成功,返回json
            .successHandler((request,response,authentication) -> {
                Map<String,Object> map = new HashMap<String,Object>();
                map.put("code",200);
                map.put("message","登录成功");
                map.put("data",authentication);
                response.setContentType("application/json;charset=utf-8");
                PrintWriter out = response.getWriter();
                out.write(objectMapper.writeValueAsString(map));
                out.flush();
                out.close();
            })
            .and()
            .exceptionHandling()
            //没有权限,返回json
            .accessDeniedHandler((request,response,ex) -> {
                response.setContentType("application/json;charset=utf-8");
                response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                PrintWriter out = response.getWriter();
                Map<String,Object> map = new HashMap<String,Object>();
                map.put("code",403);
                map.put("message", "权限不足");
                out.write(objectMapper.writeValueAsString(map));
                out.flush();
                out.close();
            })
            .and()
            .logout()
            //退出成功,返回json
            .logoutSuccessHandler((request,response,authentication) -> {
                Map<String,Object> map = new HashMap<String,Object>();
                map.put("code",200);
                map.put("message","退出成功");
                map.put("data",authentication);
                response.setContentType("application/json;charset=utf-8");
                PrintWriter out = response.getWriter();
                out.write(objectMapper.writeValueAsString(map));
                out.flush();
                out.close();
            })
            .permitAll();
            //开启跨域访问
            http.cors().disable();
            //开启模拟请求,比如API POST测试工具的测试,不开启时,API POST为报403错误
            http.csrf().disable();
    }
    
    @Override
    public void configure(WebSecurity web) {
        //对于在header里面增加token等类似情况,放行所有OPTIONS请求。
        web.ignoring().antMatchers(HttpMethod.OPTIONS, "/**");
    }

    @Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        //对默认的UserDetailsService进行覆盖
        authenticationProvider.setUserDetailsService(myCustomUserService);
        authenticationProvider.setPasswordEncoder(myPasswordEncoder);
        return authenticationProvider;
    }
    
}

La troisième étape consiste à implémenter l'interface UserDetailsService

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.Component;
/**
 * 登录专用类
 * 自定义类,实现了UserDetailsService接口,用户登录时调用的第一类
 * @author 程就人生
 *
 */
@Component
public class MyCustomUserService implements UserDetailsService {

    /**
     * 登陆验证时,通过username获取用户的所有权限信息
     * 并返回UserDetails放到spring的全局缓存SecurityContextHolder中,以供授权器使用
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //在这里可以自己调用数据库,对username进行查询,看看在数据库中是否存在
        MyUserDetails myUserDetail = new MyUserDetails();
        myUserDetail.setUsername(username);
        myUserDetail.setPassword("123456");
        return myUserDetail;
    }
}

Description : Cette classe est principalement utilisée pour recevoir le nom d'utilisateur transmis par la connexion, puis elle peut être étendue ici pour demander si le nom d'utilisateur existe dans la base de données. S'il n'existe pas, une exception peut être levée. Par souci de démonstration, ce test écrit les données à mort.

La quatrième étape consiste à implémenter l'interface PasswordEncoder

import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
/**
 * 自定义的密码加密方法,实现了PasswordEncoder接口
 * @author 程就人生
 *
 */
@Component
public class MyPasswordEncoder implements PasswordEncoder {

    @Override
    public String encode(CharSequence charSequence) {
        //加密方法可以根据自己的需要修改
        return charSequence.toString();
    }

    @Override
    public boolean matches(CharSequence charSequence, String s) {
        return encode(charSequence).equals(s);
    }
}

Description : Cette classe est principalement utilisée pour traiter le cryptage des mots de passe et comparer le mot de passe transmis par l'utilisateur avec le mot de passe de la base de données (le mot de passe dans UserDetailsService).

La cinquième étape consiste à implémenter l'interface UserDetails

import java.util.Collection;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

/**
 * 实现了UserDetails接口,只留必需的属性,也可添加自己需要的属性
 * @author 程就人生
 *
 */
@Component
public class MyUserDetails implements UserDetails {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    //登录用户名
    private String username;
    //登录密码
    private String password;

    private Collection<? extends GrantedAuthority> authorities;

    public void setUsername(String username) {
        this.username = username;
    }

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

    public void setAuthorities(Collection<? extends GrantedAuthority> authorities) {
        this.authorities = authorities;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return this.authorities;
    }

    @Override
    public String getPassword() {
        return this.password;
    }

    @Override
    public String getUsername() {
        return this.username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

Description : Cette classe est utilisée pour stocker les données utilisateur après une connexion réussie. Après une connexion réussie, vous pouvez utiliser le code suivant pour obtenir :

MyUserDetails myUserDetails= (MyUserDetails) SecurityContextHolder.getContext().getAuthentication() .getPrincipal();

Une fois le code écrit, vous devez ensuite le tester. Ce n'est qu'après le test que vous pourrez prouver la validité du code. Utilisons d'abord un navigateur.

La première étape du test consiste à visiter l'index avant de se connecter, et la page est directement redirigée vers la page de connexion par défaut, et l'interface de test est OK.

Figure 1

Dans la deuxième étape du test, après la connexion, les données json sont renvoyées et le résultat du test est OK.

Figure 2

La troisième étape consiste à tester, accéder à l'index, renvoyer les données de connexion de sortie et le résultat du test est OK.

image 3

La quatrième étape consiste à accéder à la déconnexion, à renvoyer les données json et à tester l'interface correctement.

Figure 4

La cinquième étape consiste à tester avec l'API POST, à utiliser cet outil pour simuler une requête ajax et à voir le résultat de la requête. Tout d'abord, visitez l'index, accessible uniquement après vous être connecté. Le résultat du test est correct et les données au format JSON dont nous avons besoin sont renvoyées.

Figure 5

La sixième étape, dans la boîte de dialogue de simulation de connexion, définissez la variable d'environnement pour conserver l'état de connexion.

Figure 6

**L'étape 7, test de connexion, renvoie les données au format JSON et le résultat du test est OK.

 

Figure 7

La huitième étape consiste à revenir à la fenêtre de test d'index, à envoyer une requête et à renvoyer les informations de l'utilisateur actuel au format JSON, et le résultat du test est OK.

Figure 8

Étape 9 : Quittez le test, renvoyez les données au format JSON et le résultat du test est OK

Figure-9

La dixième étape, après être sorti, visitez à nouveau l'index, il y a un problème, les informations de connexion sont toujours là, REGARDEZ !

Figure-10

 

Supprimez la coche devant l'en-tête, c'est-à-dire supprimez le cookie. C'est normal à ce moment-là. La raison est très simple. Lors de la sortie, le cookie n'est pas effacé. Cela ne peut être testé que dans un environnement formel. Quelle que soit la simulation de l'API POST, elle reste différente de l'environnement formel.

Si une erreur 403 est signalée dans le test API POST, vous devez ajouter le fichier de configuration de configuration

//开启跨域访问
http.cors().disable();
//开启模拟请求,比如API POST测试工具的测试,不开启时,API POST为报403错误
http.csrf().disable();

 

Je suppose que tu aimes

Origine blog.csdn.net/zs319428/article/details/107089473
conseillé
Classement