微服務 第九章 springboot 使用Spring Security 安全驗證


1、在springboot基礎上添加maven:

 <!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-web -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>5.3.1.RELEASE</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework.security/spring-security-config -->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>5.3.1.RELEASE</version>
        </dependency>

2、啟動,啟動日志:Using generated security password: 9141f795-55b8-4433-ae85-77c47a56e079   用戶名是user

3、url請求,重定向到服務認證,輸入用戶名密碼,認證成功后重定向到請求的url。再次訪問請求url,不再認證。訪問urlhttp://localhost:8399/logout 退出。

 4、改密碼:

在application.properties添加

spring.security.user.name=yaohuiqin
spring.security.user.password=123

 

@SpringBootApplication
/*@EnableAutoConfiguration(exclude = {
        org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class
})*/
public class SpringsecurityApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringsecurityApplication.class, args);
    }

}

再啟動項目,則可以用用戶名yaohuiqin,密碼123登錄。

換一種方式:刪除application.properties文件中的用戶名密碼,添加類:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("yhq")
                .password("123").roles("admin");
    }
} 

換一種方式

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }
    
    @Bean
    protected UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("wxh").password("123").roles("admin").build());
        manager.createUser(User.withUsername("yhquser").password("123").roles("user").build());
        return manager;
    }
}

  

5、表單登錄配置

http.formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/doLogin")
                .usernameParameter("name")
                .passwordParameter("passwd")
                .defaultSuccessUrl("/index")
                .successForwardUrl("/index")
                .permitAll()
                .and()
                .logout()
                .logoutUrl("/logout")
                .logoutRequestMatcher(new AntPathRequestMatcher("/logout","POST"))
                .logoutSuccessUrl("/index")
                .deleteCookies()
                .clearAuthentication(true)
                .invalidateHttpSession(true)
                .permitAll();

  注銷登錄的配置:

  1. 默認注銷的 URL 是 /logout,是一個 GET 請求,我們可以通過 logoutUrl 方法來修改默認的注銷 URL。
  2. logoutRequestMatcher 方法不僅可以修改注銷 URL,還可以修改請求方式,實際項目中,這個方法和 logoutUrl 任意設置一個即可。
  3. logoutSuccessUrl 表示注銷成功后要跳轉的頁面。
  4. deleteCookies 用來清除 cookie。
  5. clearAuthentication 和 invalidateHttpSession 分別表示清除認證信息和使 HttpSession 失效,默認可以不用配置,默認就會清除。

 6、Spring Security 結合 Jwt 實現無狀態登錄

  6.1 jwt認證和session認證

    JWT,全稱是 Json Web Token , 是一種 JSON 風格的輕量級的授權和身份認證規范,可實現無狀態、分布式的 Web 應用授權:

      JWT屬於無狀態認證,支持集群化部署,服務端可以任意遷移,減少服務端存儲session壓力,多平台跨域。流程:用戶發送用戶名和密碼,服務端驗證成功后用戶信息加密並且編碼成一個 token給客戶端,客戶端每次請求攜帶token,服務端接收請求時會解密token再驗證token是否有,效獲取用戶登錄信息,再根據授權獲取受保護的資源

      有以下幾個方法可以做到失效 JWT token(后期再考慮不同方式的優缺點)

  1. 將 token 存入 DB(如 Redis)中,失效則刪除;但增加了一個每次校驗時候都要先從 DB 中查詢 token 是否存在的步驟,而且違背了 JWT 的無狀態原則(這不就和 session 一樣了么?)。
  2. 維護一個 token 黑名單,失效則加入黑名單中。
  3. 在 JWT 中增加一個版本號字段,失效則改變該版本號。
  4. 在服務端設置加密的 key 時,為每個用戶生成唯一的 key,失效則改變該 key。

 7、Spring Security 配置

在前后端分離這樣的開發架構下,前后端的交互都是通過 JSON 來進行,無論登錄成功還是失敗,都不會有什么服務端跳轉或者客戶端跳轉之類。

登錄成功了,服務端就返回一段登錄成功的提示 JSON 給前端,前端收到之后,該跳轉該展示,由前端自己決定,就和后端沒有關系了。

登錄失敗了,服務端就返回一段登錄失敗的提示 JSON 給前端,前端收到之后,該跳轉該展示,由前端自己決定,也和后端沒有關系了。

package com.yhq.springsecurity.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
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.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import java.io.PrintWriter;

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //不同情況下的配置,用來記錄
        http.authorizeRequests()
                .antMatchers("/", "/index").permitAll()
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/loginindex")
                .permitAll()
                .and()
                .logout()
                .permitAll();


        http.formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/doLogin")
                .usernameParameter("name")
                .passwordParameter("passwd")
                .defaultSuccessUrl("/index")
                .successForwardUrl("/index")
                .permitAll()
                .and()
                .logout()
                .logoutUrl("/logout")
                .logoutRequestMatcher(new AntPathRequestMatcher("/logout", "POST"))
                .logoutSuccessUrl("/index")
                .deleteCookies()
                .clearAuthentication(true)
                .invalidateHttpSession(true)
                .permitAll();

        //統統 JSON 交互
        http.formLogin().successHandler((req, resp, authentication) -> {
            Object principal = authentication.getPrincipal();
            resp.setContentType("application/json;charset=utf-8");
            PrintWriter out = resp.getWriter();
            out.write(new ObjectMapper().writeValueAsString(principal));
            out.flush();
            out.close();
        }).and().formLogin().failureHandler((req, resp, e) -> {
            resp.setContentType("application/json;charset=utf-8");
            PrintWriter out = resp.getWriter();
            out.write(e.getMessage());
            out.flush();
            out.close();
        }).and().csrf().disable().exceptionHandling()
                .authenticationEntryPoint((req, resp, authException) -> {
                            resp.setContentType("application/json;charset=utf-8");
                            PrintWriter out = resp.getWriter();
                            out.write("尚未登錄,請先登錄");
                            out.flush();
                            out.close();
        }).and().logout()
                .logoutUrl("/logout")
                .logoutSuccessHandler((req, resp, authentication) -> {
                    resp.setContentType("application/json;charset=utf-8");
                    PrintWriter out = resp.getWriter();
                    out.write("注銷成功");
                    out.flush();
                    out.close();
                })
                .permitAll();


    }

    @Bean
    @Override
    public UserDetailsService userDetailsService() {
        UserDetails user =
                User.withDefaultPasswordEncoder()
                        .username("user")
                        .password("password")
                        .roles("USER")
                        .build();
        PasswordEncoder passwordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();

        UserDetails user2 = User
                .withUsername("yun")
                .password(passwordEncoder.encode("123456"))
                .roles("USER")
                .build();
        return new InMemoryUserDetailsManager(user2);
    }
}

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM