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即可
github示例
@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();
}
}
相关文章
SpringSecurity入门(二)
SpringSecurity入门(三)
SpringSecurity入门(四)
未完待续