Segurança integrada SpringBoot (4) | (A segurança é baseada em JWT para obter separação de front-end e login personalizado)

Segurança integrada SpringBoot (4) | (A segurança é baseada em JWT para obter separação de front-end e login personalizado)


Capítulo
Capítulo 1 link: SpringBoot integrado de segurança (1) | (Entrada de segurança)
Capítulo 2 link: SpringBoot integrado de segurança (2) | (configuração personalizada de segurança)
Capítulo 3 link: SpringBoot integrado de segurança (3) | (segurança Front-end e back-end -end separação de login e processamento de resposta)
Link do capítulo 4: segurança integrada SpringBoot (4) | (A segurança é baseada em JWT para obter separação de front-end e login personalizado)

prefácio

No capítulo anterior, apresentamos a configuração de usuário baseada em segurança do springboot, configuração de permissão e configuração de recurso. E reescrevemos o formulário de login para fazer login, e o relatório de erros de autenticação e exceções de autorização foi tratado de maneira uniforme, mas ainda existem alguns problemas. Os projetos são todos separados do front-end e do back-end. Neste capítulo, nós implementará o login JWT com base na separação dos front-ends e back-ends.

Este artigo é uma expansão com base no anterior. Se você não está muito claro sobre a base do projeto, consulte o artigo anterior

1. Dependências do projeto

Inclui principalmente dependências de segurança e algumas dependências de ferramentas

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.7</version>
        <relativePath/>
    </parent>
   <dependencies>
       <!--    springboot   start-->
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter</artifactId>
       </dependency>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-test</artifactId>
           <scope>test</scope>
       </dependency>
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-web</artifactId>
       </dependency>
       <!--    springboot依赖    end-->

       <!--wagger2依赖start-->
       <dependency>
           <groupId>com.github.xiaoymin</groupId>
           <artifactId>knife4j-spring-ui</artifactId>
           <version>3.0.3</version>
       </dependency>
       <dependency>
           <groupId>io.springfox</groupId>
           <artifactId>springfox-swagger2</artifactId>
           <version>3.0.0</version>
       </dependency>

       <!--常用工具依赖start-->
       <dependency>
           <groupId>org.apache.commons</groupId>
           <artifactId>commons-lang3</artifactId>
           <version>3.12.0</version>
       </dependency>

       <dependency>
           <groupId>org.apache.commons</groupId>
           <artifactId>commons-collections4</artifactId>
           <version>4.1</version>
       </dependency>

       <dependency>
           <groupId>com.google.guava</groupId>
           <artifactId>guava</artifactId>
           <version>30.1.1-jre</version>
       </dependency>

       <!--fastjson引入-->
       <dependency>
           <groupId>com.alibaba</groupId>
           <artifactId>fastjson</artifactId>
           <version>1.2.49</version>
       </dependency>
       <dependency>
           <groupId>org.projectlombok</groupId>
           <artifactId>lombok</artifactId>
           <optional>true</optional>
       </dependency>
       <dependency>
           <groupId>commons-codec</groupId>
           <artifactId>commons-codec</artifactId>
           <version>1.15</version>
       </dependency>

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

       <!--数据库引入引入-->
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-data-jpa</artifactId>
       </dependency>
       <dependency>
           <groupId>mysql</groupId>
           <artifactId>mysql-connector-java</artifactId>
           <scope>runtime</scope>
       </dependency>
       <!--jwt引入-->
       <dependency>
           <groupId>io.jsonwebtoken</groupId>
           <artifactId>jjwt</artifactId>
           <version>0.9.1</version>
       </dependency>

       <dependency>
           <groupId>joda-time</groupId>
           <artifactId>joda-time</artifactId>
           <version>2.9.9</version>
       </dependency>
   </dependencies>
   

2. Processamento de resposta personalizada

O processamento de resposta personalizada é principalmente para definir o formato de resposta, o que é conveniente para a coordenação de front-end e back-end

1. Defina o corpo da resposta ResponseHandle

@Data
public class ResponseHandle<T> {
    
    
    private String status;
    private String desc;
    private T data;

    // 成功 无参构成函数
    public static ResponseHandle SUCCESS(){
    
    
        ResponseHandle result = new ResponseHandle();
        result.setDesc("成功");
        result.setResultCode(HttpCode.SUCCESS);
        return result;
    }
    //成功 有返回数据构造函数
    public static ResponseHandle SUCCESS(Object data){
    
    
        ResponseHandle result = new ResponseHandle();
        result.setData(data);
        result.setResultCode(HttpCode.SUCCESS);
        return result;
    }

    /**
     * 失败,指定status、desc
     */
    public static ResponseHandle FAIL(String status, String desc) {
    
    
        ResponseHandle result = new ResponseHandle();
        result.setStatus(status);
        result.setDesc(desc);
        return result;
    }

    /**
     * 失败,指定ResultCode枚举
     */
    public static ResponseHandle FAIL(HttpCode resultCode) {
    
    
        ResponseHandle result = new ResponseHandle();
        result.setResultCode(resultCode);
        return result;
    }
    /**
     * 把ResultCode枚举转换为ResResult
     */
    private void setResultCode(HttpCode code) {
    
    
        this.status = code.code();
        this.desc = code.message();
    }
}

2. Defina a enumeração de resposta HttpCode

public enum HttpCode {
    
    

    // 成功状态码
    SUCCESS("00000", "成功"),
    UNKNOWN_ERROR("99999", "服务未知异常"),
    // 系统500错误
    SYSTEM_ERROR("10000", "系统异常,请稍后重试"),


    // 认证错误:20001-29999
    USER_NOAUTH("20000", "用户未登录"),
    TOKEN_ERROR("20001", "生成token失败"),
    LOGIN_ERROR("20002", "登录失败"),
    USER_LOCKED("20004", "账户已锁定"),
    USER_PASS_OUT("20005", "用户名或密码错误次数过多"),
    USER_NOTFIND_ERROR("20006", "没有找到用户"),
    USER_ERROR("20007", "用户名或密码不正确"),
    USER_CODE("20008", "验证码输入有误,请重新输入!"),
    USER_DISABLE("20009", "该账号也被禁用,请联系管理员!"),
    USER_INFOERROR("20010", "用户信息获取异常!"),
    USER_NOAUTHON("20011", "用户没有权限访问"),

    ;


    private String code;

    private String message;

    HttpCode(String code, String message) {
    
    
        this.code = code;
        this.message = message;
    }

    public String code() {
    
    
        return this.code;
    }

    public String message() {
    
    
        return this.message;
    }
}

Três, classe de resposta de login de usuário personalizada

Principalmente quando um usuário acessa recursos, verifica-se se o usuário não está logado, e o formato das informações de front-end retornadas no cenário onde o usuário está logado e não possui direitos de acesso

1. Avisar que o usuário não está logado

@Component
public class RestfulAccessDeniedHandler implements AccessDeniedHandler {
    
    
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
    
    
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        response.setStatus(HttpServletResponse.SC_OK);
        ResponseHandle fail = ResponseHandle.FAIL(HttpCode.USER_NOAUTHON);
        response.getWriter().write(JSONObject.toJSONString(fail));
    }
}

2. O usuário não tem direitos de acesso para processar

@Component
public class RestAuthorizationEntryPoint implements AuthenticationEntryPoint {
    
    

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
    
    
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        response.setStatus(HttpServletResponse.SC_OK);
        ResponseHandle fail = ResponseHandle.FAIL(HttpCode.USER_NOAUTH);
        response.getWriter().write(JSONObject.toJSONString(fail));
    }
}

Quarto, implementação de login personalizado

1. Introdução ao JwtAuthencationTokenFilter

O filtro BasicAuthenticationFilter tem o mesmo efeito que o filtro OncePerRequestFilter. É um filtro de segurança para implementar a autenticação de login do usuário. Altere o filtro para selecionar quando o usuário visitar. Podemos herdar essa classe e, em seguida, reescrever o método doFilterInternal para implementar personalizado interceptação de token, de modo que toda vez que a interface acessar o recurso, o token será recebido para verificar se ele é válido e expirado.

2. Realização da interface de login de nome de usuário e senha personalizados

Aqui, os usuários podem personalizar sua própria lógica de implementação, como criptografia de senha de usuário, parâmetros de retorno, etc.

1. Interface de login

@Api(tags = {
    
    "登录相关接口"})
@RestController
@RequestMapping("/oak")
public class LoginCtrl {
    
    

    @Autowired
    private LoginService loginService;

    @ApiOperation(value = "用户登录接口", notes = "登录")
    @PostMapping("/login")
    public ResponseHandle login(@RequestBody User user, HttpServletRequest request) {
    
    
        return loginService.login(user, request);
    }
}

2. Faça login para implementar o servidor

@Service
public class LoginServiceImpl implements LoginService {
    
    

    @Autowired
    private UserServiceImpl userDetailsService;

    @Autowired
    private PasswordEncoder passwordEncoder;


    @Override
    public ResponseHandle login(User user, HttpServletRequest request) {
    
    
        String username = user.getUsername();
        String password = user.getPassword();

//        String code = user.getCode();
//        // 验证码
//        String captcha = (String) request.getSession().getAttribute("captcha");
//        // 判断验证码
//        if ("".equals(code) || !captcha.equalsIgnoreCase(code)) {
    
    
//            return ResponseHandle.FAIL(HttpCode.USER_CODE);
//        }
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);
        if (userDetails.isEnabled()) {
    
    
            if (null == userDetails || !passwordEncoder.matches(password, userDetails.getPassword())) {
    
    
                return ResponseHandle.FAIL(HttpCode.USER_ERROR);
            }
            // 更新security登录用户对象
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
//            authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            //将authenticationToken放入spring security全局中
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            // 创建一个token
            String token = JwtTokenUtils.createToken(username, "", true);
            Map<String, String> tokenMap = new HashMap<>();
            tokenMap.put("token", "Bearer" + token);
            return ResponseHandle.SUCCESS(tokenMap);
        }
        return ResponseHandle.FAIL(HttpCode.USER_DISABLE);
    }
}

3. Consultar usuários por nome de usuário

@Service
public class UserServiceImpl implements UserDetailsService {
    
    

    @Autowired
    private UserRepository userRepository;

    /**
     *
     * @param s
     * @return 实现loadUserByUsername方法,根据用户名查找用户信息
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
    
    
        User user = userRepository.findByUsername(s);
        return new JwtUser(user);
    }
}

4. O JPA realiza a consulta do banco de dados do usuário

public interface UserRepository extends CrudRepository<User, Integer> {
    
    
    /**
     * 根据用户名查询用户
     * @param username
     * @return
     */
    User findByUsername(String username);
}

5. Entidade do usuário

@Data
@Entity
@Table(name = "t_user")
public class User {
    
    

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Integer id;

    @Column(name = "username")
    private String username;

    @Column(name = "password")
    private String password;

    @Column(name = "code")
    private String code;

    @Column(name = "role")
    private String role;

    @Override
    public String toString() {
    
    
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", role='" + role + '\'' +
                '}';
    }
}

3. Configuração de segurança

Configure a fonte do usuário de segurança, modo de autenticação, filtro, etc. Aqui usamos nossa própria interface de login, então precisamos deixá-la de lado e não usar o modo de envio de formulário

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
    

    @Autowired
    private UserServiceImpl userService;

    @Resource
    private RestAuthorizationEntryPoint restAuthorizationEntryPoint;

    @Resource
    private RestfulAccessDeniedHandler restfulAccessDeniedHandler;



    /**
     * 常用的三种存储方式,项目找那个用的最多的为,自定义用户存储
     *
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    
    
        //1、内存用户配置
//        auth.inMemoryAuthentication().passwordEncoder(bCryptPasswordEncoder())
//                .withUser("admin").password(bCryptPasswordEncoder().encode("123456")).authorities("ADMIN")
//                .and()
//                .withUser("test").password(bCryptPasswordEncoder().encode("123456")).authorities("TEST");
        //2、数据库用户配置
//        auth.jdbcAuthentication().dataSource(dataSource).passwordEncoder(passwordEncoder())
//                .usersByUsernameQuery(
//                        "select username, password, status from Users where username = ?")
//                .authoritiesByUsernameQuery(
//                        "select username, authority from Authority where username = ?");
        //3、自定义用户存储
        auth.userDetailsService(userService)
                .passwordEncoder(bCryptPasswordEncoder());
    }

    /**
     * configure(WebSecurity)用于影响全局安全性(配置资源,设置调试模式,通过实现自定义防火墙定义拒绝请求)的配置设置。
     * 一般用于配置全局的某些通用事物,例如静态资源等
     *
     * @param web
     */
    @Override
    public void configure(WebSecurity web) {
    
    
        web.ignoring()
                .antMatchers(HttpMethod.OPTIONS, "/**")  ///跨域请求预处理
                .antMatchers("/favicon.ico")
                .antMatchers("/swagger**")   // 以下swagger静态资源、接口不拦截
                .antMatchers("/doc.html")
                .antMatchers("/swagger-resources/**")
                .antMatchers("/v2/api-docs")
                .antMatchers("/webjars/**")
//                .antMatchers("/logout")
                .antMatchers("/js/**", "/css/**", "/images/**");  // 排除html静态资源
    }

    /**
     * 配置接口拦截
     * configure(HttpSecurity)允许基于选择匹配在资源级配置基于网络的安全性,
     * 也就是对角色所能访问的接口做出限制
     *
     * @param httpSecurity 请求属性
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
    
    
        httpSecurity
                .authorizeRequests()
                .antMatchers(HttpMethod.GET, "/demo/get").permitAll()
                .antMatchers("/oak/login", "/oak/logout").permitAll()
                //指定权限为ROLE_ADMIN才能访问,这里和方法注解配置效果一样,但是会覆盖注解
                .antMatchers("/demo/delete").hasRole("ADMIN")
                // 所有请求都需要验证
                .anyRequest().authenticated()
                .and()
                .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class)
                .exceptionHandling()
                .accessDeniedHandler(restfulAccessDeniedHandler)
                .authenticationEntryPoint(restAuthorizationEntryPoint)
                .and()
                .csrf().disable()
                .sessionManagement()//禁用session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .logout().logoutUrl("/logout")
                .and()
                // 禁用缓存
                .headers()
                .cacheControl();
    }


    /**
     * 配置用户认证方式
     *
     * @return
     * @throws Exception
     */
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
    
    
        return super.authenticationManagerBean();
    }


    /**
     * 自定义过滤器,用来替换security的默认过滤器(UsernamePasswordAuthenticationFilter),
     * 实现自定义的login接口,接口路径为了区别默认的/login我们定义为/mylogin
     *
     * @return
     * @throws Exception
     */

    /**
     * 使用security 提供的加密规则(还有其他加密方式)
     *
     * @return
     */
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
    
    
        return new BCryptPasswordEncoder();
    }

    /**
     * JWT token过滤器
     * @return
     * @throws Exception
     */
    @Bean
    public JwtAuthencationTokenFilter jwtAuthenticationFilter() throws Exception {
    
    
        JwtAuthencationTokenFilter jwtAuthenticationFilter = new JwtAuthencationTokenFilter(authenticationManager());
        return jwtAuthenticationFilter;
    }
}

4. Personalize a implementação de JwtAuthencationTokenFilter

Configurar fontes de usuário de segurança, modos de autenticação, filtros, etc. Aqui definimos nossa própria interface de login /mylogin

public class JwtAuthencationTokenFilter extends BasicAuthenticationFilter {
    
    
    private String tokenHeader = "Authorization";
    private String tokenHead = "Bearer";

    @Autowired
    private UserServiceImpl userService;

    public JwtAuthencationTokenFilter(AuthenticationManager authenticationManager) {
    
    
        super(authenticationManager);
    }

    /**
     * 自定义过滤器,用来校验token是否存在,token是否失效
     *
     * @param request
     * @param response
     * @param filterChain
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
    
    
        // 请求头中获取token信息
        String authheader = request.getHeader(tokenHeader);
        // 存在token
        if (null != authheader && authheader.startsWith(tokenHead)) {
    
    
            // 去除字段名称, 获取真正token
            String authToken = authheader.substring(tokenHead.length());
            // 利用token获取用户名
            String username = JwtTokenUtils.getUsername(authToken);

            System.out.println("自定义JWT过滤器获得用户名为" + username);
            Authentication a = SecurityContextHolder.getContext().getAuthentication();

            // token存在用户未登陆
            // SecurityContextHolder.getContext().getAuthentication() 获取上下文对象中认证信息
            if (null != username && null == SecurityContextHolder.getContext().getAuthentication()) {
    
    
                // 自定义数据源获取用户信息
                UserDetails userDetails = userService.loadUserByUsername(username);
                // 验证token是否有效 验证token用户名和存储的用户名是否一致以及是否在有效期内, 重新设置用户对象
//                if (JwtTokenUtils.isExpiration(authToken)) {
    
    
                // 重新将用户信息封装到UsernamePasswordAuthenticationToken
                if (JwtTokenUtils.checkToken(authToken)) {
    
    
                    System.out.println("token有效");
                    UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                    authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                    SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                }
            }
        }
        //继续下一个拦截器
        filterChain.doFilter(request, response);
    }
}

4. Classe de ferramenta JwtTokenUtils

É usado principalmente para configurar e gerar tokens, verificar tokens e outros métodos relacionados

public class JwtTokenUtils {
    
    


    private static final String SECRET = "oak-secret";
    private static final String ISS = "oak";

    /**
     * 角色的key
     */
    private static final String ROLE_CLAIMS = "rol";

    /**
     * 过期时间是3600秒,既是1个小时
     */
    private static final long EXPIRATION = 3600L;

    /**
     * 选择了记住我之后的过期时间为7天
     */
    private static final long EXPIRATION_REMEMBER = 604800L;

    /**
     * 创建token
     *
     * @param username
     * @param role
     * @param isRememberMe
     * @return
     */
    public static String createToken(String username, String role, boolean isRememberMe) {
    
    
        long expiration = isRememberMe ? EXPIRATION_REMEMBER : EXPIRATION;
        HashMap<String, Object> map = new HashMap<>();
        map.put(ROLE_CLAIMS, role);
        return Jwts.builder()
                .signWith(SignatureAlgorithm.HS512, SECRET)
                .setClaims(map)  //存放自定义信息,也可不放
                .setIssuer(ISS)     //发行人
                .setSubject(username)   //jwt主题
                .setIssuedAt(new Date())   //当前时间
                .setExpiration(new Date(System.currentTimeMillis() + expiration * 1000)) // 过期时间
                .compact();
    }

    /**
     * 从token中获取用户名
     *
     * @param token
     * @return
     */
    public static String getUsername(String token) {
    
    
        return getTokenBody(token).getSubject();
    }


    /**
     * 获取用户角色
     *
     * @param token
     * @return
     */
    public static String getUserRole(String token) {
    
    
        return (String) getTokenBody(token).get(ROLE_CLAIMS);
    }

    /**
     * 是否已过期
     *
     * @param token
     * @return
     */
    public static boolean isExpiration(String token) {
    
    
        try {
    
    
            return getTokenBody(token).getExpiration().before(new Date());
        } catch (ExpiredJwtException e) {
    
    
            return true;
        }
    }


    /**
     * 根据token,判断token是否存在与有效
     *
     * @param jwtToken
     * @return
     */
    public static boolean checkToken(String jwtToken) {
    
    
        if (StringUtils.isEmpty(jwtToken)) return false;
        try {
    
    
            Jwts.parser().setSigningKey(SECRET).parseClaimsJws(jwtToken);
        } catch (Exception e) {
    
    
            e.printStackTrace();
            return false;
        }
        return true;
    }

    private static Claims getTokenBody(String token) {
    
    
        return Jwts.parser()
                .setSigningKey(SECRET)
                .parseClaimsJws(token)
                .getBody();
    }
}

4. Verifique o login

1. Escreva a interface de teste

@Api(tags = {
    
    "演示相关接口"})
@RestController
@RequestMapping("/demo")
public class DemoCtrl {
    
    

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private BCryptPasswordEncoder bCryptPasswordEncoder;


    @ApiOperation(value = "获取接口", notes = "获取接口")
    @GetMapping(value = "/get")
    public ResponseHandle get() {
    
    
        return ResponseHandle.SUCCESS("获取数据成功");
    }

    @ApiOperation(value = "更新接口(ADMIN可访问)", notes = "更新接口")
    @GetMapping(value = "/update1")
    @PreAuthorize("hasAnyRole('ROLE_ADMIN')")
    public ResponseHandle update1() {
    
    
        return ResponseHandle.SUCCESS("更新数据成功");
    }

    @ApiOperation(value = "查询接口(USER可访问)", notes = "查询接口")
    @GetMapping(value = "/find")
    @PreAuthorize("hasAnyRole('ROLE_USER')")
    public ResponseHandle find() {
    
    
        return ResponseHandle.SUCCESS("查询数据成功");
    }

    @ApiOperation(value = "删除用户(ADMIN配置可用)", notes = "修改")
    @GetMapping("/delete")
    public ResponseHandle delete() {
    
    
        return ResponseHandle.SUCCESS("删除成功");
    }

    @ApiOperation(value = "注册用户", notes = "注册")
    @PostMapping("/register")
    public String registerUser(@RequestBody Map<String, String> registerUser) {
    
    
        User user = new User();
        user.setUsername(registerUser.get("username"));
        user.setPassword(bCryptPasswordEncoder.encode(registerUser.get("password")));
        user.setRole("ROLE_USER");
        User save = userRepository.save(user);
        return save.toString();
    }
}

2. Solicitar interface de chamada

1. Chame a interface de chamada de login.
Adicione uma descrição da imagem
Você pode ver que a interface retorna uma longa string de tokens. Da próxima vez que acessarmos os recursos do serviço, precisamos apenas trazer o token alterado.

2. Chame a interface de exclusão
Adicione uma descrição da imagem> 3. Mude para um usuário cuja função seja USER para chamar a interface de exclusãoAdicione uma descrição da imagem

Resumir

Nesse ponto, o springboot integra a segurança para concluir a configuração do banco de dados do usuário e o login personalizado e melhora as respostas relacionadas ao login, o que facilita o processamento unificado dos front-ends e back-ends. Este tipo de login pode atender às necessidades de um único projeto, mas considerando a autenticação de login entre vários projetos, ainda existem muitos problemas nesta solução. Em seguida, continuaremos a concluir o método de autenticação baseado no token OAuth2

Links para o Capítulo 1: SpringBoot Integrated Security (1) | (Introdução à Segurança)
Links para o Capítulo 2: SpringBoot Integrated Security (2) | (Configuração Personalizada de Segurança)
Links para o Capítulo 3: SpringBoot Integrated Security (3) | (Antes e Depois Segurança Processamento de login e resposta separados por extremidade)
Link do Capítulo 4: Segurança integrada do SpringBoot (4) | (A segurança é baseada em JWT para obter separação de front-end e login personalizado)

Acho que você gosta

Origin blog.csdn.net/Oaklkm/article/details/128190531
Recomendado
Clasificación