1、引入依賴 spring-boot版本2.7.3,如未特殊說明版本預設使用此版本 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactI ...
1、引入依賴
spring-boot版本2.7.3,如未特殊說明版本預設使用此版本
<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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</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>
2、編寫controller並啟動springboot服務
@RestController
public class HelloController {
@GetMapping("/")
public String hello(){
return "hello SpringSecurity";
}
}
- 啟動
- 訪問http://localhost:8080/
- 登陸使用賬號:user,密碼:04e74f23-0e97-4ee9-957e-2004a2e60692
- SecurityProperties
3、自動配置SpringBootWebSecurityConfiguration
- SecurityFilterChainConfiguration
- WebSecurityConfigurerAdapter中有所有的Security相關的配置,只需要繼承重新對應屬性即可完成自定義
- 由於新版本的Security已經棄用WebSecurityConfigurerAdapter所以註冊SecurityFilterChain即可
@Bean
SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
return httpSecurity.authorizeRequests()
.mvcMatchers("/index").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.and().build();
}
4、預設登陸頁面DefaultLoginPageGeneratingFilter
4.1、SecurityFilterChainConfiguration預設實現的SecurityFilterChain
- 容器中沒有WebSecurityConfigurerAdapter類型的bean實例自動配置才會生效
4.2、UsernamePasswordAuthenticationFilter
4.3、attemptAuthentication方法
4.4、 ProviderManager的authenticate方法
4.5、 AuthenticationProvider實現AbstractUserDetailsAuthenticationProvider中的authenticate方法
4.6、 UserDetails實現類DaoAuthenticationProvider的retrieveUser方法
4.7、UserDetailsService實現類InMemoryUserDetailsManager的loadUserByUsername方法
4.8、 UserDetailsService
4.9、 UserDetailsServiceAutoConfiguration
- 容器中沒有:AuthenticationManager、AuthenticationProvider、UserDetailsService、AuthenticationManagerResolver這4個bean實例才會載入InMemoryUserDetailsManager
4.10、 SecurityProperties
- 可以通過spring.security.user.password=123456自定義密碼
5、 自定義認證
5.1、由於新版本的Security已經棄用WebSecurityConfigurerAdapter所以註冊SecurityFilterChain即可
@Bean
SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
return httpSecurity.authorizeRequests()
.mvcMatchers("/index").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.and().build();
}
5.2、 自定義登陸頁面
5.2.1、html
- 使用UsernamePasswordAuthenticationFilter用戶名和密碼欄位名必須是username和password,且必須是POST的方式提交
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body>
<form th:action="@{/doLogin}" method="post">
<p>用戶名:<label>
<input name="username" type="text"/>
</label></p>
<p>密碼:<label>
<input name="password" type="password"/>
</label></p>
<p>
<input type="submit">
</p>
</form>
</body>
</html>
5.2.2、SecurityFilterChain配置
@Configuration
public class WebSecurityConfigurer {
@Bean
SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
return
//開啟許可權驗證
httpSecurity.authorizeRequests()
//permitAll直接放行,必須在anyRequest().authenticated()前面
.mvcMatchers("/toLogin").permitAll()
.mvcMatchers("/index").permitAll()
//anyRequest所有請求都需要認證
.anyRequest().authenticated()
.and()
//使用form表單驗證
.formLogin()
//自定義登陸頁面
.loginPage("/toLogin")
//自定義登陸頁面後必須指定處理登陸請求的url
.loginProcessingUrl("/doLogin")
.and()
//禁止csrf跨站請求保護
.csrf().disable()
.build();
}
5.2.3、 controller
@Controller
public class LoginController {
@RequestMapping("toLogin")
public String toLogin(){
return "login";
}
}
5.2.4、 自定義登陸使用的用戶名和密碼欄位名使用usernameParameter和passwordParameter
@Configuration
public class WebSecurityConfigurer {
@Bean
SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
return
//開啟許可權驗證
httpSecurity.authorizeRequests()
//permitAll直接放行,必須在anyRequest().authenticated()前面
.mvcMatchers("/toLogin").permitAll()
.mvcMatchers("/index").permitAll()
//anyRequest所有請求都需要認證
.anyRequest().authenticated()
.and()
//使用form表單驗證
.formLogin()
//自定義登陸頁面
.loginPage("/toLogin")
//自定義登陸頁面後必須指定處理登陸請求的url
.loginProcessingUrl("/doLogin")
// 自定義接收用戶名的參數名為uname
.usernameParameter("uname")
// 自定義接收密碼的參數名為pwd
.passwordParameter("pwd")
.and()
//禁止csrf跨站請求保護
.csrf().disable()
.build();
}
}
- form表單中對應參數名也需要修改,用戶名為:uname,密碼為:pwd
5.3、 自定義認證成功後訪問的頁面
- successForwardUrl(轉發),必須使用POST請求,每次都會跳轉到指定請求
- defaultSuccessUrl(重定向),必須使用GET請求,不會每次都跳轉定義的頁面,預設會記錄認證攔截的請求,如果是攔截的受限資源會優先跳轉到之前被攔截的請求。需要每次都跳轉使用.defaultSuccessUrl("/test",true)即可
- 二選一
// 登陸認證成功後跳轉的頁面(轉發),必須使用POST請求
// .successForwardUrl("/test")
// 陸認證成功後跳轉的頁面(重定向),必須使用GET請求
.defaultSuccessUrl("/test",true)
5.4、 前後端分離處理方式
5.4.1、 實現AuthenticationSuccessHandler介面
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
Map<String,Object> map = new HashMap<>();
map.put("msg", "登陸成功");
map.put("code", HttpStatus.OK);
map.put("authentication", authentication);
String s = new ObjectMapper().writeValueAsString(map);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(s);
}
}
5.4.2、修改SecurityFilterChain配置
- 使用successHandler(new MyAuthenticationSuccessHandler())
@Configuration
public class WebSecurityConfigurer {
@Bean
@SuppressWarnings("all")
SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
return
//開啟許可權驗證
httpSecurity.authorizeRequests()
//permitAll直接放行,必須在anyRequest().authenticated()前面
.mvcMatchers("/toLogin").permitAll()
.mvcMatchers("/index").permitAll()
//anyRequest所有請求都需要認證
.anyRequest().authenticated()
.and()
//使用form表單驗證
.formLogin()
//自定義登陸頁面
.loginPage("/toLogin")
//自定義登陸頁面後必須指定處理登陸請求的url
.loginProcessingUrl("/doLogin")
// 自定義接收用戶名的參數名為uname
.usernameParameter("uname")
// 自定義接收密碼的參數名為pwd
.passwordParameter("pwd")
// 登陸認證成功後跳轉的頁面(轉發),必須使用POST請求
// .successForwardUrl("/test")
// 陸認證成功後跳轉的頁面(轉發),必須使用GET請求
// .defaultSuccessUrl("/test",true)
//不會每次都跳轉定義的頁面,預設會記錄認證攔截的請求,如果是攔截的受限資源會優先跳轉到之前被攔截的請求。需要每次都跳轉使defaultSuccessUrl("/test",true)
// .defaultSuccessUrl("/test")
// 前後端分離時代自定義認證成功處理
.successHandler(new MyAuthenticationSuccessHandler())
.and()
//禁止csrf跨站請求保護
.csrf().disable()
.build();
}
}
5.4.3、返回數據
6、 認證失敗處理
- failureForwardUrl,轉發,請求必須是POST
- failureUrl,重定向,請求必須是GET
6.1、org.springframework.security.authentication.ProviderManager#authenticate
6.2、 org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter#doFilter(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, javax.servlet.FilterChain)
6.3、 org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter#unsuccessfulAuthentication
6.4、 org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler#onAuthenticationFailure
6.5、 org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler#saveException
- 如果是轉發異常信息存在request裡面
- 如果是重定向異常信息存在session裡面,預設是重定向
- 參數名:SPRING_SECURITY_LAST_EXCEPTION
6.7、 前端取值展示
- 修改SecurityFilterChain配置
@Configuration
public class WebSecurityConfigurer {
@Bean
@SuppressWarnings("all")
SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
return
//開啟許可權驗證
httpSecurity.authorizeRequests()
//permitAll直接放行,必須在anyRequest().authenticated()前面
.mvcMatchers("/toLogin").permitAll()
.mvcMatchers("/index").permitAll()
//anyRequest所有請求都需要認證
.anyRequest().authenticated()
.and()
//使用form表單驗證
.formLogin()
//自定義登陸頁面
.loginPage("/toLogin")
//自定義登陸頁面後必須指定處理登陸請求的url
.loginProcessingUrl("/doLogin")
// 自定義接收用戶名的參數名為uname
.usernameParameter("uname")
// 自定義接收密碼的參數名為pwd
.passwordParameter("pwd")
// 登陸認證成功後跳轉的頁面(轉發),必須使用POST請求
// .successForwardUrl("/test")
// 陸認證成功後跳轉的頁面(轉發),必須使用GET請求
// .defaultSuccessUrl("/test",true)
//不會每次都跳轉定義的頁面,預設會記錄認證攔截的請求,如果是攔截的受限資源會優先跳轉到之前被攔截的請求。需要每次都跳轉使defaultSuccessUrl("/test",true)
// .defaultSuccessUrl("/test")
// 前後端分離時代自定義認證成功處理
.successHandler(new MyAuthenticationSuccessHandler())
// 認證失敗跳轉頁面,必須使用POST請求
.failureForwardUrl("/toLogin")
// 認證失敗跳轉頁面,,必須使用GET請求
// .failureUrl("/toLogin")
.and()
//禁止csrf跨站請求保護
.csrf().disable()
.build();
}
}
- html增加取值
<!-- 重定向錯誤信息存在session中 -->
<p th:text="${session.SPRING_SECURITY_LAST_EXCEPTION}"></p>
<!-- 轉發錯誤信息存在request中 -->
<p th:text="${SPRING_SECURITY_LAST_EXCEPTION}"></p>
6.8、 前後端分離處理方式
6.8.1、 實現AuthenticationFailureHandler介面
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
Map<String,Object> map = new HashMap<>();
map.put("msg", exception.getMessage());
map.put("code", HttpStatus.INTERNAL_SERVER_ERROR.value());
String s = new ObjectMapper().writeValueAsString(map);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(s);
}
}
6.8.2、修改SecurityFilterChain配置
- failureHandler
@Configuration
public class WebSecurityConfigurer {
@Bean
@SuppressWarnings("all")
SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
return
//開啟許可權驗證
httpSecurity.authorizeRequests()
//permitAll直接放行,必須在anyRequest().authenticated()前面
.mvcMatchers("/toLogin").permitAll()
.mvcMatchers("/index").permitAll()
//anyRequest所有請求都需要認證
.anyRequest().authenticated()
.and()
//使用form表單驗證
.formLogin()
//自定義登陸頁面
.loginPage("/toLogin")
//自定義登陸頁面後必須指定處理登陸請求的url
.loginProcessingUrl("/doLogin")
// 自定義接收用戶名的參數名為uname
.usernameParameter("uname")
// 自定義接收密碼的參數名為pwd
.passwordParameter("pwd")
// 登陸認證成功後跳轉的頁面(轉發),必須使用POST請求
// .successForwardUrl("/test")
// 陸認證成功後跳轉的頁面(轉發),必須使用GET請求
// .defaultSuccessUrl("/test",true)
//不會每次都跳轉定義的頁面,預設會記錄認證攔截的請求,如果是攔截的受限資源會優先跳轉到之前被攔截的請求。需要每次都跳轉使defaultSuccessUrl("/test",true)
// .defaultSuccessUrl("/test")
// 前後端分離時代自定義認證成功處理
.successHandler(new MyAuthenticationSuccessHandler())
// 認證失敗跳轉頁面,必須使用POST請求
// .failureForwardUrl("/toLogin")
// 認證失敗跳轉頁面,必須使用GET請求
// .failureUrl("/toLogin")
// 前後端分離時代自定義認證失敗處理
.failureHandler(new MyAuthenticationFailureHandler())
.and()
//禁止csrf跨站請求保護
.csrf().disable()
.build();
}
}
7、 註銷登錄
7.1、 預設方式
.logout()
// 指定註銷url,預設請求方式GET
.logoutUrl("/logout")
// 註銷成功後跳轉頁面
.logoutSuccessUrl("/toLogin")
@Configuration
public class WebSecurityConfigurer {
@Bean
@SuppressWarnings("all")
SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
return
//開啟許可權驗證
httpSecurity.authorizeRequests()
//permitAll直接放行,必須在anyRequest().authenticated()前面
.mvcMatchers("/toLogin").permitAll()
.mvcMatchers("/index").permitAll()
//anyRequest所有請求都需要認證
.anyRequest().authenticated()
.and()
//使用form表單驗證
.formLogin()
//自定義登陸頁面
.loginPage("/toLogin")
//自定義登陸頁面後必須指定處理登陸請求的url
.loginProcessingUrl("/doLogin")
// 自定義接收用戶名的參數名為uname
.usernameParameter("uname")
// 自定義接收密碼的參數名為pwd
.passwordParameter("pwd")
// 登陸認證成功後跳轉的頁面(轉發),必須使用POST請求
// .successForwardUrl("/test")
// 陸認證成功後跳轉的頁面(轉發),必須使用GET請求
// .defaultSuccessUrl("/test",true)
//不會每次都跳轉定義的頁面,預設會記錄認證攔截的請求,如果是攔截的受限資源會優先跳轉到之前被攔截的請求。需要每次都跳轉使defaultSuccessUrl("/test",true)
// .defaultSuccessUrl("/test")
// 前後端分離時代自定義認證成功處理
.successHandler(new MyAuthenticationSuccessHandler())
// 認證失敗跳轉頁面,必須使用POST請求
// .failureForwardUrl("/toLogin")
// 認證失敗跳轉頁面,必須使用GET請求
// .failureUrl("/toLogin")
// 前後端分離時代自定義認證失敗處理
.failureHandler(new MyAuthenticationFailureHandler())
.and()
// 註銷
.logout()
// 指定註銷url,預設請求方式GET
.logoutUrl("/logout")
// 銷毀session,預設為true
.invalidateHttpSession(true)
// 清除認證信息,預設為true
.clearAuthentication(true)
// 註銷成功後跳轉頁面
.logoutSuccessUrl("/toLogin")
.and()
//禁止csrf跨站請求保護
.csrf().disable()
.build();
}
}
7.2、 自定義方式
// 註銷
.logout()
// 自定義註銷url
.logoutRequestMatcher(newOrRequestMatcher(
newAntPathRequestMatcher("/aa","GET"),
newAntPathRequestMatcher("/bb","POST")
))
// 註銷成功後跳轉頁面
.logoutSuccessUrl("/toLogin")
@Configuration
public class WebSecurityConfigurer {
@Bean
@SuppressWarnings("all")
SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
return
//開啟許可權驗證
httpSecurity.authorizeRequests()
//permitAll直接放行,必須在anyRequest().authenticated()前面
.mvcMatchers("/toLogin").permitAll()
.mvcMatchers("/index").permitAll()
//anyRequest所有請求都需要認證
.anyRequest().authenticated()
.and()
//使用form表單驗證
.formLogin()
//自定義登陸頁面
.loginPage("/toLogin")
//自定義登陸頁面後必須指定處理登陸請求的url
.loginProcessingUrl("/doLogin")
// 自定義接收用戶名的參數名為uname
.usernameParameter("uname")
// 自定義接收密碼的參數名為pwd
.passwordParameter("pwd")
// 登陸認證成功後跳轉的頁面(轉發),必須使用POST請求
// .successForwardUrl("/test")
// 陸認證成功後跳轉的頁面(轉發),必須使用GET請求
// .defaultSuccessUrl("/test",true)
//不會每次都跳轉定義的頁面,預設會記錄認證攔截的請求,如果是攔截的受限資源會優先跳轉到之前被攔截的請求。需要每次都跳轉使defaultSuccessUrl("/test",true)
// .defaultSuccessUrl("/test")
// 前後端分離時代自定義認證成功處理
.successHandler(new MyAuthenticationSuccessHandler())
// 認證失敗跳轉頁面,必須使用POST請求
// .failureForwardUrl("/toLogin")
// 認證失敗跳轉頁面,必須使用GET請求
// .failureUrl("/toLogin")
// 前後端分離時代自定義認證失敗處理
.failureHandler(new MyAuthenticationFailureHandler())
.and()
// 註銷
.logout()
// 指定註銷url,預設請求方式GET
// .logoutUrl("/logout")
.logoutRequestMatcher(new OrRequestMatcher(
new AntPathRequestMatcher("/aa","GET"),
new AntPathRequestMatcher("/bb","POST")
))
// 銷毀session,預設為true
.invalidateHttpSession(true)
// 清除認證信息,預設為true
.clearAuthentication(true)
// 註銷成功後跳轉頁面
.logoutSuccessUrl("/toLogin")
.and()
//禁止csrf跨站請求保護
.csrf().disable()
.build();
}
}
7.3、 前後端分離
7.3.1、 實現LogoutSuccessHandler介面
public class MyLogoutSuccessHandler implements LogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
Map<String,Object> map = new HashMap<>();
map.put("msg", "註銷成功");
map.put("code", HttpStatus.OK.value());
map.put("authentication", authentication);
String s = new ObjectMapper().writeValueAsString(map);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(s);
}
}
7.3.2、 修改SecurityFilterChain配置
- logoutSuccessHandler
@Configuration
public class WebSecurityConfigurer {
@Bean
@SuppressWarnings("all")
SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
return
//開啟許可權驗證
httpSecurity.authorizeRequests()
//permitAll直接放行,必須在anyRequest().authenticated()前面
.mvcMatchers("/toLogin").permitAll()
.mvcMatchers("/index").permitAll()
//anyRequest所有請求都需要認證
.anyRequest().authenticated()
.and()
//使用form表單驗證
.formLogin()
//自定義登陸頁面
.loginPage("/toLogin")
//自定義登陸頁面後必須指定處理登陸請求的url
.loginProcessingUrl("/doLogin")
// 自定義接收用戶名的參數名為uname
.usernameParameter("uname")
// 自定義接收密碼的參數名為pwd
.passwordParameter("pwd")
// 登陸認證成功後跳轉的頁面(轉發),必須使用POST請求
// .successForwardUrl("/test")
// 陸認證成功後跳轉的頁面(轉發),必須使用GET請求
// .defaultSuccessUrl("/test",true)
//不會每次都跳轉定義的頁面,預設會記錄認證攔截的請求,如果是攔截的受限資源會優先跳轉到之前被攔截的請求。需要每次都跳轉使defaultSuccessUrl("/test",true)
// .defaultSuccessUrl("/test")
// 前後端分離時代自定義認證成功處理
.successHandler(new MyAuthenticationSuccessHandler())
// 認證失敗跳轉頁面,必須使用POST請求
// .failureForwardUrl("/toLogin")
// 認證失敗跳轉頁面,必須使用GET請求
// .failureUrl("/toLogin")
// 前後端分離時代自定義認證失敗處理
.failureHandler(new MyAuthenticationFailureHandler())
.and()
// 註銷
.logout()
// 指定預設註銷url,預設請求方式GET
// .logoutUrl("/logout")
// 自定義註銷url
.logoutRequestMatcher(new OrRequestMatcher(
new AntPathRequestMatcher("/aa","GET"),
new AntPathRequestMatcher("/bb","POST")
))
// 銷毀session,預設為true
.invalidateHttpSession(true)
// 清除認證信息,預設為true
.clearAuthentication(true)
// 註銷成功後跳轉頁面
// .logoutSuccessUrl("/toLogin")
.logoutSuccessHandler(new MyLogoutSuccessHandler())
.and()
//禁止csrf跨站請求保護
.csrf().disable()
.build();
}
}
8、獲取用戶認證信息
- 三種策略模式,調整通過修改VM options
// 如果沒有設置自定義的策略,就採用MODE_THREADLOCAL模式
public static final String MODE_THREADLOCAL = "MODE_THREADLOCAL";
// 採用InheritableThreadLocal,它是ThreadLocal的一個子類,適用多線程的環境
public static final String MODE_INHERITABLETHREADLOCAL = "MODE_INHERITABLETHREADLOCAL";
// 全局策略,實現方式就是static SecurityContext contextHolder
public static final String MODE_GLOBAL = "MODE_GLOBAL";
-Dspring.security.strategy=MODE_INHERITABLETHREADLOCAL
8.1、 使用代碼獲取
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
User user = (User) authentication.getPrincipal();
System.out.println("身份信息:" + authentication.getPrincipal());
System.out.println("用戶:" + user.getUsername());
System.out.println("許可權信息:" + authentication.getAuthorities());
8.2、 前端頁面獲取
8.2.1、 引入依賴
- 不需要版本號
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
8.2.2、 導入命名空間,獲取數據
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body>
<form th:action="@{/bb}" method="post">
<p>
<input type="submit" value="註銷登陸">
</p>
</form>
<hr>
<h2>獲取認證用戶信息</h2>
<ul>
<!-- <li sec:authentication="name"></li>-->
<!-- <li sec:authentication="authorities"></li>-->
<!-- <li sec:authentication="credentials"></li>-->
<!-- <li sec:authentication="authenticated"></li>-->
<li sec:authentication="principal.username"></li>
<li sec:authentication="principal.authorities"></li>
<li sec:authentication="principal.accountNonExpired"></li>
<li sec:authentication="principal.accountNonLocked"></li>
<li sec:authentication="principal.credentialsNonExpired"></li>
</ul>
</body>
</html>
9、 自定義數據源
9.1、 流程分析
When the user submits their credentials, the AbstractAuthenticationProcessingFilter creates an Authentication from the HttpServletRequest to be authenticated. The type of Authentication created depends on the subclass of AbstractAuthenticationProcessingFilter. For example, UsernamePasswordAuthenticationFilter creates a UsernamePasswordAuthenticationToken from a username and password that are submitted in the HttpServletRequest.
Next, the Authentication is passed into the AuthenticationManager to be authenticated.
If authentication fails, then Failure
- The SecurityContextHolder is cleared out.
- RememberMeServices.loginFail is invoked. If remember me is not configured, this is a no-op.
- AuthenticationFailureHandler is invoked.
If authentication is successful, then Success.
- SessionAuthenticationStrategy is notified of a new log in.
- The Authentication is set on the SecurityContextHolder. Later the SecurityContextPersistenceFilter saves the SecurityContext to the HttpSession.
- RememberMeServices.loginSuccess is invoked. If remember me is not configured, this is a no-op.
- ApplicationEventPublisher publishes an InteractiveAuthenticationSuccessEvent.
- AuthenticationSuccessHandler is invoked.
9.2、 修改WebSecurityConfigurer
@Autowired
DataSource dataSource;
@Bean
public PasswordEncoder bcryptPasswordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
public UserDetailsService users(PasswordEncoder encoder) {
// The builder will ensure the passwords are encoded before saving in memory
UserDetails user = User.withUsername("user")
.password(encoder.encode("123"))
.roles("USER")
.build();
UserDetails admin = User.withUsername("admin")
.password(encoder.encode("123"))
.roles("USER", "ADMIN")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
public UserDetailsService users() {
JdbcUserDetailsManager users = new JdbcUserDetailsManager(dataSource);
// UserDetails admin = User.withUsername("齊豐")
// .password(encoder.encode("123456"))
// .roles("USER")
// .build();
// users.createUser(admin);
System.out.println(dataSource.getClass());
return users;
}
9.3、 In-Memory Authentication
- 修改SecurityFilterChain
- 通過指定userDetailsService來切換不同的認證數據儲存方式
@Bean
@SuppressWarnings("all")
SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
return
//開啟許可權驗證
httpSecurity
.authorizeRequests()
//permitAll直接放行,必須在anyRequest().authenticated()前面
.mvcMatchers("/toLogin").permitAll()
.mvcMatchers("/index").permitAll()
//anyRequest所有請求都需要認證
.anyRequest().authenticated()
.and()
//使用form表單驗證
.formLogin()
//自定義登陸頁面
.loginPage("/toLogin")
//自定義登陸頁面後必須指定處理登陸請求的url
.loginProcessingUrl("/doLogin")
// 自定義接收用戶名的參數名為uname
.usernameParameter("uname")
// 自定義接收密碼的參數名為pwd
.passwordParameter("pwd")
// 登陸認證成功後跳轉的頁面(轉發),必須使用POST請求
// .successForwardUrl("/test")
// 陸認證成功後跳轉的頁面(轉發),必須使用GET請求
// .defaultSuccessUrl("/test",true)
//不會每次都跳轉定義的頁面,預設會記錄認證攔截的請求,如果是攔截的受限資源會優先跳轉到之前被攔截的請求。需要每次都跳轉使defaultSuccessUrl("/test",true)
// .defaultSuccessUrl("/test")
// 前後端分離時代自定義認證成功處理
.successHandler(new MyAuthenticationSuccessHandler())
// 認證失敗跳轉頁面,必須使用POST請求
// .failureForwardUrl("/toLogin")
// 認證失敗跳轉頁面,必須使用GET請求
// .failureUrl("/toLogin")
// 前後端分離時代自定義認證失敗處理
.failureHandler(new MyAuthenticationFailureHandler())
.and()
// 註銷
.logout(