Verificación de inicio de sesión de separación frontal y posterior de SpringBoot Security

Verificación de inicio de sesión de separación frontal y posterior de SpringBoot Security

Este artículo se reproduce. Muy bien escrito, fácil de entender.

 

Recientemente, estuve investigando el uso de OAuth2, pensando en el inicio de sesión único completo, encontré muchas demostraciones en Internet pero no pude implementarlas, tal vez porque no entiendo el principio de OAuth2 en absoluto. Durante unos días, me estaba volviendo cada vez más despistado y no podía rendirme. Giré la cabeza y pensé: OAuth2 es una extensión basada en la seguridad, y no sé nada sobre seguridad, así que vamos. Estudie Seguridad primero, desarrolle Seguridad y descubra cómo se siente.

Simplemente hágalo y use Seguridad en el entorno SpringBoot 2.1.4.RELEASE ya preparado.
Sin mencionar el uso de seguridad simple. El proyecto actual está separado del front-end y back-end. El formato de datos devuelto después de un inicio de sesión exitoso o fallido debe estar en formato JSON. También debe devolver un mensaje de aviso en formato JSON cuando no está registrado. También necesita devolver JSON al cerrar sesión. Independientemente de la autorización, primero devuelva los datos en formato JSON. Esto está hecho. También lo he estudiado durante varios días y leí muchas experiencias de otras personas. Investiga.

A continuación, el código:

El primer paso es introducir el archivo de configuración de Seguridad en pom.xml

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

El segundo paso es aumentar el archivo de configuración de configuración.

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;
    }
    
}

El tercer paso es implementar la interfaz 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;
    }
}

Descripción: esta clase se utiliza principalmente para recibir el nombre de usuario pasado al iniciar sesión, y luego se puede extender aquí para consultar si el nombre de usuario existe en la base de datos. Si no existe, se puede lanzar una excepción. A modo de demostración, esta prueba escribe los datos hasta el final.

El cuarto paso es implementar la interfaz 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);
    }
}

Descripción: esta clase se utiliza principalmente para procesar el cifrado de contraseñas y comparar la contraseña pasada por el usuario con la contraseña de la base de datos (la contraseña en UserDetailsService).

El quinto paso es implementar la interfaz 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;
    }
}

Descripción: esta clase se utiliza para almacenar datos del usuario después de un inicio de sesión exitoso. Después de iniciar sesión exitosamente, puede usar el siguiente código para obtener:

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

Una vez escrito el código, debe probarlo a continuación. Sólo después de la prueba podrá demostrar la validez del código. Utilicemos primero un navegador.

La primera prueba es visitar el índice antes de iniciar sesión, la página se redirige directamente a la página de inicio de sesión predeterminada y la interfaz de prueba está bien.

Figura 1

En el segundo paso de la prueba, después de iniciar sesión, se devuelven los datos json y el resultado de la prueba es correcto.

Figura 2

El tercer paso es probar, acceder al índice, devolver los datos de inicio de sesión de salida y el resultado de la prueba es correcto.

imagen 3

El cuarto paso es acceder al cierre de sesión, devolver datos json y probar la interfaz correctamente.

Figura 4

El quinto paso es probar con API POST, usar esta herramienta para simular la solicitud ajax y ver el resultado de la solicitud. Primero, visite el índice, al que solo se puede acceder después de iniciar sesión. El resultado de la prueba es correcto y se devuelven los datos en formato JSON que necesitamos.

Figura 5

El sexto paso, en el cuadro de diálogo de simulación de inicio de sesión, configure la variable de entorno para mantener el estado de inicio de sesión.

Figura 6

**Paso 7, prueba de inicio de sesión, devuelve datos en formato JSON y el resultado de la prueba es correcto.

 

Figura-7

El octavo paso es regresar a la ventana de prueba de índice, enviar una solicitud y devolver la información del usuario actual en formato JSON, y el resultado de la prueba es correcto.

Figura 8

Paso 9: salga de la prueba, devuelva datos en formato JSON y el resultado de la prueba será correcto

Figura-9

El décimo paso, después de salir, visite el índice nuevamente, hay un problema, la información de inicio de sesión todavía está ahí, ¡MIRA!

Figura-10

 

Quite la marca delante del encabezado, es decir, elimine la cookie. Es normal en este momento. La razón es muy simple. Al salir, la cookie no se borra. Esto solo se puede probar en un entorno formal. No importa cuán simulado sea API POST, sigue siendo diferente del entorno formal.

Si se informa un error 403 en la prueba API POST, debe agregar el archivo de configuración

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

 

Supongo que te gusta

Origin blog.csdn.net/zs319428/article/details/107089473
Recomendado
Clasificación