Проверка входа в систему SpringBoot Security с разделением на переднюю и внутреннюю части

Проверка входа в систему SpringBoot Security с разделением на переднюю и внутреннюю части

Эта статья воспроизводится. Очень хорошо написано, легко понять

 

В последнее время я исследовал использование OAuth2, размышляя о едином входе в целом, нашел много демо из Интернета, но реализовать его не удалось, возможно потому, что вообще не понимаю принцип OAuth2. В течение нескольких дней я терял все больше и больше невежества и не мог сдаться. Я повернул голову и подумал: OAuth2 — это расширение на основе безопасности, а я ничего не знаю о безопасности, так что давайте Сначала изучите безопасность. Повысьте безопасность и узнайте, каково это.

Просто сделайте это и используйте Security в готовой среде SpringBoot 2.1.4.RELEASE.
Не говоря уже об использовании простой безопасности. Текущий проект отделен от внешней и внутренней частей. Формат данных, возвращаемый после успешного входа в систему или сбоя, должен быть в формате JSON. Он также должен возвращать подсказку в формате JSON, если он не зарегистрирован. in. Он также должен возвращать JSON при выходе из системы. Отформатированные данные. Независимо от авторизации, сначала возвращайте данные в формате JSON. На этом дело сделано. Я тоже несколько дней изучал и читал много чужого опыта. Исследуйте.

Ниже код:

Первым шагом является введение файла конфигурации безопасности в pom.xml.

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

Вторым шагом является увеличение файла конфигурации 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;
    }
    
}

Третий шаг — реализация интерфейса 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;
    }
}

Описание: Этот класс в основном используется для получения имени пользователя, передаваемого при входе в систему, а затем его можно расширить, чтобы запросить, существует ли имя пользователя в базе данных. Если оно не существует, может быть выдано исключение. Этот тест записывает данные на смерть для демонстрации.

Четвертый шаг — реализация интерфейса 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);
    }
}

Описание. Этот класс в основном используется для обработки шифрования паролей и сравнения пароля, переданного пользователем, с паролем базы данных (паролем в UserDetailsService).

Пятый шаг — реализация интерфейса 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;
    }
}

Описание: Этот класс используется для хранения пользовательских данных после успешного входа в систему.После успешного входа в систему вы можете использовать следующий код для получения:

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

После того, как код написан, вам необходимо протестировать его. Только после теста вы сможете доказать валидность кода. Давайте сначала воспользуемся браузером.

Первый тест — посетить индекс перед входом в систему, страница напрямую перенаправляется на страницу входа по умолчанию, и тестовый интерфейс в порядке.

Рисунок 1

На втором этапе теста после входа в систему возвращаются данные json, и результат теста в порядке.

фигура 2

Третий шаг — тестирование, доступ к индексу, возврат выходных данных для входа и результат теста — в порядке.

изображение 3

Четвертый шаг — доступ к выходу из системы, возврат данных в формате JSON и проверка работоспособности интерфейса.

Рисунок 4

Пятый шаг — протестировать API POST, использовать этот инструмент для имитации запроса ajax и просмотреть результат запроса.Сначала посетите индекс, доступ к которому можно получить только после входа в систему. Результат теста в порядке, и возвращаются нужные нам данные в формате JSON.

Рисунок 5

На шестом шаге в диалоговом окне имитации входа задайте переменную среды, чтобы сохранить статус входа.

Рисунок 6

**Шаг 7, проверка входа в систему, возвращает данные в формате JSON, результат проверки — в порядке.

 

Рисунок-7

Восьмой шаг — вернуться в окно теста индекса, отправить запрос и вернуть информацию текущего пользователя в формате JSON, результат теста — в порядке.

Рисунок-8

Шаг 9. Выйдите из теста, верните данные в формате JSON, результат теста в порядке.

Рисунок-9

Десятый шаг, после выхода снова посетите индекс, есть проблема, данные для входа все еще там, ПОСМОТРИТЕ!

Рисунок-10

 

Убрать галочку перед заголовком, то есть удалить куки. Это нормально на данный момент. Причина очень проста. При выходе куки не очищаются. Это можно проверить только в формальной среде. Независимо от того, насколько смоделирован API POST, он все равно отличается от формальной среды.

Если в тесте API POST сообщается об ошибке 403, вам необходимо добавить файл конфигурации конфигурации.

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

 

рекомендация

отblog.csdn.net/zs319428/article/details/107089473