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();
注銷登錄的配置:
- 默認注銷的 URL 是
/logout,是一個 GET 請求,我們可以通過 logoutUrl 方法來修改默認的注銷 URL。 - logoutRequestMatcher 方法不僅可以修改注銷 URL,還可以修改請求方式,實際項目中,這個方法和 logoutUrl 任意設置一個即可。
- logoutSuccessUrl 表示注銷成功后要跳轉的頁面。
- deleteCookies 用來清除 cookie。
- 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(后期再考慮不同方式的優缺點)
- 將 token 存入 DB(如 Redis)中,失效則刪除;但增加了一個每次校驗時候都要先從 DB 中查詢 token 是否存在的步驟,而且違背了 JWT 的無狀態原則(這不就和 session 一樣了么?)。
- 維護一個 token 黑名單,失效則加入黑名單中。
- 在 JWT 中增加一個版本號字段,失效則改變該版本號。
- 在服務端設置加密的 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);
}
}
