Erste Schritte mit Spring Boot + Spring Security Basic

Einführung in Spring Security

Spring Security ist ein leistungsstarkes und hochgradig anpassbares Authentifizierungs- und Zugriffskontroll-Framework. Spring Security konzentriert sich auf die Bereitstellung von Authentifizierungs- und Autorisierungsfunktionen für Java-Anwendungen.

Spring Security hat zwei wichtige Kernfunktionen: Benutzerauthentifizierung (Authentication) und Benutzerautorisierung (Authorization) .

Benutzerauthentifizierung: Überprüfen Sie, ob ein Benutzer ein legitimes Subjekt im System ist, dh ob der Benutzer auf das System zugreifen kann. Die Benutzerauthentifizierung erfordert im Allgemeinen, dass der Benutzer einen Benutzernamen und ein Passwort bereitstellt. Das System schließt den Authentifizierungsprozess ab, indem es den Benutzernamen und das Kennwort überprüft.

Benutzerautorisierung: Stellen Sie sicher, dass ein Benutzer die Berechtigung zum Ausführen einer Operation hat. In einem System haben verschiedene Benutzer unterschiedliche Berechtigungen. Beispielsweise können einige Benutzer eine Datei nur lesen, während einige Benutzer sie sowohl lesen als auch ändern können. Im Allgemeinen weist das System verschiedenen Benutzern unterschiedliche Rollen zu, und jede Rolle entspricht einer Reihe von Berechtigungen.

Vorbereitung

Erstellen Sie ein Spring Boot-Projekt

pom.xml-Datei (eingeführt nach Ihren Bedürfnissen)

    <dependencies>
        <!-- security(安全认证) -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- mybatis-plus(数据库操作) -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.2</version>
        </dependency>
        <!-- redis(缓存) -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

        <!-- swagger(api接口文档) -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>

        <!-- jjwt(token生成与校验) -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        <!-- fastjson2(JSON处理) -->
        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
            <version>2.0.23</version>
        </dependency>

        <!-- mysql(连接驱动) -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- druid(mysql连接池) -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.16</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

Authentifizierung _

Login-Verifizierungsprozess

Voruntersuchung des Prinzips

Das Prinzip von Spring Security ist eigentlich eine Filterkette, die Filter enthält, die verschiedene Funktionen bereitstellen. Hier können wir uns den Filter im Starter-Beispiel ansehen.

In der Figur sind nur Kernfilter gezeigt, und andere Nicht-Kernfilter sind in der Figur nicht gezeigt.

UsernamePasswordAuthenticationFilter : Verantwortlich für die Verarbeitung der Anmeldeanfrage, nachdem wir den Benutzernamen und das Passwort auf der Anmeldeseite eingegeben haben. Sie ist hauptsächlich für die Zertifizierungsarbeit des Eingangsfalls zuständig.

ExceptionTranslationFilter: Behandelt alle AccessDeniedException und AuthenticationException, die in der Filterkette ausgelöst werden.

FilterSecurityInterceptor: Ein Filter, der für die Berechtigungsüberprüfung verantwortlich ist.

Wir können sehen, welche Filter und ihre Reihenfolge sich in der Spring Security-Filterkette im aktuellen System über Debug befinden.

Klicken Sie auf der Konsole auf Ausdruck auswerten oder Alt+F8, wie unten gezeigt:

 Geben Sie dann run.getBean(DefaultSecurityFilterChain.class) ein, um zu filtern, und Sie können 15 Filter im Run-Container sehen:

 

Spring Security-Konfigurationsklasse

import com.zm.springsecurity.common.filter.CustomAuthenticationFilter;
import com.zm.springsecurity.common.security.CustomAuthenticationFailureHandler;
import com.zm.springsecurity.common.security.CustomAuthenticationSuccessHandler;
import com.zm.springsecurity.common.security.CustomLogoutSuccessHandler;
import com.zm.springsecurity.service.impl.CustomUserDetailsServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true) // 开启方法级安全
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    private static final String URL_WHITELIST[] ={
            "/v2/api-docs", "/swagger-resources/configuration/ui",
            "/swagger-resources", "/swagger-resources/configuration/security",
            "/swagger-ui.html", "/webjars/**", // swagger不需要授权即可访问的路径

            "/login",
            "/logout",
            "/my/login",
            "/my/logout",
            "/captcha",
            "/password",
            "/image/**",
            "/test/**"
    };

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Bean
    protected CustomAuthenticationFilter customAuthenticationFilter() throws Exception {
        CustomAuthenticationFilter authenticationFilter = new CustomAuthenticationFilter();
        authenticationFilter.setFilterProcessesUrl("/my/login");
        authenticationFilter.setUsernameParameter("username");
        authenticationFilter.setPasswordParameter("password");
        authenticationFilter.setAuthenticationManager(super.authenticationManager());
        authenticationFilter.setAuthenticationSuccessHandler(new CustomAuthenticationSuccessHandler());
        authenticationFilter.setAuthenticationFailureHandler(new CustomAuthenticationFailureHandler());
        return authenticationFilter;
    }

//    @Override
//    @Bean
//    public AuthenticationManager authenticationManagerBean() throws Exception {
//        return super.authenticationManagerBean();
//    }
//
//    @Override
//    protected AuthenticationManager authenticationManager() throws Exception {
//        return super.authenticationManager();
//    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable() // 开启跨域请求和关闭csrf攻击
                .userDetailsService(new CustomUserDetailsServiceImpl())
//                .formLogin().loginPage("/login_page")
//                .loginProcessingUrl("/my/login")
//                .usernameParameter("username").passwordParameter("password").permitAll()
//                .successHandler(new CustomAuthenticationSuccessHandler()) // 认证成功处理器
//                .failureHandler(new CustomAuthenticationFailureHandler()) // 认证失败处理器
//                .and()
                .logout()
                .logoutUrl("/my/logout")
                .logoutSuccessHandler(new CustomLogoutSuccessHandler()) // 退出登录成功处理器
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS) // session禁用配置(无状态)
                .and()
                .authorizeRequests()  // 验证请求拦截规则
                .antMatchers(URL_WHITELIST).permitAll() // 配置访问认证白名单
                .antMatchers("/admin/**").hasRole("admin") // 要具有某种权限
                .antMatchers("/user/**").hasAnyRole("admin", "user") // 要具有某种权限中的一种
                .anyRequest().authenticated();

        http.addFilterAt(this.customAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }
}

Authentifizierung mit einer Datenbank

Hinweis: Dieser Artikel verwendet MyBatis-Plus als Persistenzschicht-Framework, und der Inhalt, der sich auf MyBatis-Plus bezieht, wird von ihm selbst geschrieben.

Implementieren Sie die UserDetails-Schnittstelle

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

import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;

public class CustomUserDetails implements UserDetails {
    private User user;
    private List<SimpleGrantedAuthority> authorityList;

    public CustomUserDetails() {
    }

    public CustomUserDetails(User user, List<String> roleList) {
        this.user = user;
        this.authorityList = roleList.stream()
                .map(role -> new SimpleGrantedAuthority(role))
                .collect(Collectors.toList());
    }

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

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

    @Override
    public String getUsername() {
        return user.getPassword();
    }

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

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

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

    @Override
    public boolean isEnabled() {
        return user.getStatus();
    }
}

Benutzerdefinierter UsernamePasswordAuthenticationFilter-Filter

Wenn es sich bei der Verarbeitungsanforderung um Daten vom Typ Formular handelt, ignoriert und löscht dieser Schritt den Inhalt, der sich auf CustomAuthenticationFilter in der Sicherheitskonfigurationsklasse bezieht. UsernamePasswordAuthenticationFilter ist ein Authentifizierungsfilter. Standardmäßig kann er nur die vom Formular übermittelten Daten verarbeiten. Wenn Sie JSON-Daten verarbeiten müssen, müssen Sie die Methode tryAuthentication() von UsernamePasswordAuthenticationFilter überschreiben.

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;

/**
 * 登录认证过滤器,处理认证的请求体为 JSON 的数据
 */
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        String contentType = request.getContentType();
        logger.info("contentType = " + contentType);
        if (contentType.equals(MediaType.APPLICATION_JSON_VALUE) || contentType.equals(MediaType.APPLICATION_JSON_UTF8_VALUE)) {
            UsernamePasswordAuthenticationToken authRequest = null;
            try (InputStream inputStream = request.getInputStream()) {
                ObjectMapper mapper = new ObjectMapper(); // JSON数据映射器
                Map<String,String> params = mapper.readValue(inputStream, Map.class);
                authRequest = new UsernamePasswordAuthenticationToken(params.get("username"), params.get("password"));
            } catch (IOException e) {
                e.printStackTrace();
                authRequest = new UsernamePasswordAuthenticationToken("", "");
            } finally {
                setDetails(request, authRequest);
                return this.getAuthenticationManager().authenticate(authRequest);
            }
        }
        else {
            return super.attemptAuthentication(request, response);
        }
    }
}

benutzerdefinierter Prozessor

  JWT-Tool-Klasse

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.util.StringUtils;

import java.util.Date;

public class JWTUtils {
    private static final String tokenSignKey = "zm_sign_key"; // 私钥(盐),太短会报异常:secret key byte array cannot be null or empty.
    private static final Integer tokenExpiration = 60 * 60 * 24 * 14; // 14天

    public static String createToken(String username){
        String token = Jwts.builder()
                .setSubject("AUTH-USER")
                .setExpiration(new Date(System.currentTimeMillis() + tokenExpiration))
                .claim("username", username)
                .signWith(SignatureAlgorithm.HS512, tokenSignKey)
                .compact();
        return token;
    }

    public static String createToken(Long userId, String username){
        String token = Jwts.builder()
                .setSubject("AUTH-USER")
                .setExpiration(new Date(System.currentTimeMillis() + tokenExpiration))
                .claim("userId", userId)
                .claim("username", username)
                .signWith(SignatureAlgorithm.HS512, tokenSignKey)
                .compact();
        return token;
    }

    public static Long getUserId(String token) {
        try {
            if (!StringUtils.hasLength(token)) {
                return null;
            }

            Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
            Claims claims = claimsJws.getBody();
            return claims.get("userId", Long.class);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static String getUsername(String token) {
        try {
            if (!StringUtils.hasLength(token)) {
                return "";
            }

            Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
            Claims claims = claimsJws.getBody();
            return claims.get("username", String.class);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

Antworten Sie auf JSON-Dateninformationen

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class ResponseUtils {
    public static void response(HttpServletResponse response, String data) throws IOException {
        response.setContentType("text/html;charset=utf-8");
        PrintWriter responseWriter = response.getWriter();
        responseWriter.write(data);
        responseWriter.flush();
        responseWriter.close();
    }
}

Benutzerdefinierter Authentifizierungs-Erfolgshandler

import com.alibaba.fastjson2.JSON;
import com.zm.springsecurity.utils.JWTUtils;
import com.zm.springsecurity.utils.ResponseUtils;
import com.zm.springsecurity.utils.ResultUtils;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        String username = authentication.getPrincipal().toString();
        String token = JWTUtils.createToken(username);
        String jsonString = JSON.toJSONString(ResultUtils.ok("登录成功", token));
        ResponseUtils.response(response, jsonString);
    }
}

Benutzerdefinierter Handler für Authentifizierungsfehler

import com.alibaba.fastjson2.JSON;
import com.zm.springsecurity.utils.ResponseUtils;
import com.zm.springsecurity.utils.ResultUtils;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class CustomAuthenticationFailureHandler implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        String message = exception.getMessage();
        if(exception instanceof BadCredentialsException){
            message = "用户名或密码错误!";
        }
        String jsonString = JSON.toJSONString(ResultUtils.fail(message));
        ResponseUtils.response(response, jsonString);
    }
}

Benutzerdefinierter Logout-Erfolgshandler

import com.alibaba.fastjson2.JSON;
import com.zm.springsecurity.utils.ResponseUtils;
import com.zm.springsecurity.utils.ResultUtils;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class CustomLogoutSuccessHandler implements LogoutSuccessHandler {
    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        String jsonString = JSON.toJSONString(ResultUtils.ok("退出登录成功!"));
        ResponseUtils.response(response, jsonString);
    }
}

Autorisierung _ _

 

Fortgesetzt werden... 

Supongo que te gusta

Origin blog.csdn.net/weixin_57542177/article/details/126166205
Recomendado
Clasificación