SpringSecurity6.x使用
SpringSecurity版本
SpringSecurity目前支持的版本如下图所示,可以看到5.x的版本过几年就不会再维护了,6.x将成为主流。
入门
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
编写测试代码
@RestController
@RequestMapping("/test")
public class TestController {
@GetMapping("/test1")
public String test1() {
return "Spring Security Demo";
}
}
启动项目后,会跳转到下图的登录页
用户名是user,密码则会打印在控制台,输入用户名和密码以后就可以登录了。
这个页面是在DefaultLoginPageGeneratingFilter的generateLoginPageHtml方法中生成的,而密码则是在UsernamePasswordAuthenticationFilter中生成的。
配置用户和密码
yml配置
spring:
application:
name: SpringSecurityDemo
security:
user:
name: admin #用户名
password: 888888 #密码
roles: #用户具有的角色
- admin
使用配置类配置
@Bean
public UserDetailsService userDetailsService() {
//使用hash加密
UserDetails user = User.withUsername("admin")
.password("{bcrypt}admin")
.roles("USER")
.build();
//使用bcrypt加密
UserDetails root = User.withUsername("root").password(bCryptPasswordEncoder().encode("123456"))
.roles("admin", "user").build();
//密码前面需要添加{id},id加密算法的名称,不然会报错,noop是不加密
UserDetails root1 = User.withUsername("root1").password("{noop}123456")
.roles("admin", "user").build();
return new InMemoryUserDetailsManager(user, root, root1);
}
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
常用的Handler
SpirngSecurity提供了针对各种异常处理的Handler,比如登录错误处理Handler、登录成功处理Handler等,我们只需要继承这些Handler实现自定义的逻辑即可。
登录成功登录失败
登录失败处理逻辑
@Component
public class LoginFailHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("登录失败");
exception.printStackTrace();
}
}
登录成功处理逻辑
@Component
public class LoginSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
System.out.println("登录成功");
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("登录成功");
}
}
退出登录
成功退出登录时的处理逻辑
@Component
public class MyLogoutSuccessHandler implements LogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
Object principal = authentication.getPrincipal();
response.setContentType("application/json;charset=utf-8");
PrintWriter out = response.getWriter();
out.write(JSON.toJSONString(principal));
out.flush();
out.close();
}
}
授权失败
没有访问权限时的处理逻辑
@Component
public class MyAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
System.out.println("授权失败处理");
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("没有访问权限");
accessDeniedException.printStackTrace();
}
}
Spring Security配置
如果想实现更多自定义的内容,则需要对SpringSecurity进行配置,比如上面自定义的登录成功Handler需要配置后才能生效。
配置主要内容如下:
- 登录配置:设置自定义登录页、登录接口、登录成功失败处理逻辑以及认证成功后跳转路径
- 授权配置:主要是设置哪些接口不需要登录认证就能访问,哪些接口需要登录认证后才能访问
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
//表单提交
httpSecurity.formLogin(formLogin -> formLogin
//自定义登录页地址
.loginPage("/login.html")
.successHandler(loginSuccessHandler)
.failureHandler(loginFailHandler)
//登录接口
.loginProcessingUrl("/test/login")
//认证成功后跳转的路径
.defaultSuccessUrl("/index.html"));
//授权配置
httpSecurity.authorizeHttpRequests(authorize -> authorize
//设置哪些路径可以直接访问,不需认证
.requestMatchers("/login.html", "/test/login", "/swagger-ui/index.html").permitAll()
//其他的路径都需要认证
.anyRequest().authenticated());
//退出登录成功处理器
httpSecurity.logout(logout -> logout.logoutSuccessHandler(logoutSuccessHandler));
//授权失败处理配置
httpSecurity.exceptionHandling(exceptionHandling ->
exceptionHandling.accessDeniedHandler(accessDeniedHandler));
//禁用csrf
httpSecurity.csrf(csrf -> csrf.disable());
//设置自定义的UserDetailService,如果自定了UserDetailService,则需要设置这个
httpSecurity.userDetailsService(userDetailsService);
return httpSecurity.build();
}
这个配置里需要注意的是loginProcessingUrl配置,loginProcessingUrl的作用是用来拦截前端页面对/login/doLogin这个的请求的,如果拦截到了就会走自己的请求,比如MyUserDetailService的loadUserByUsername方法。
完整代码
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/test/login" method="post">
用户名:<input type="text" name="username"/><br/>
密码:<input type="password" name="password"/><br/>
<input type="submit" value="提交"/>
</form>
</body>
</html>
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
index html
</body>
</html>
UserDetailService
@Service
public class MyUserDetailService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//根据username从数据库中获取用户信息,并封装成UserDetails对象,SpringSecurity会根据返回的UserDetails和用户输入的密码进行比对
String user = "root1";
String password = "root1";
return User.withUsername(user)
.password(new BCryptPasswordEncoder().encode(password))
.roles("USER")
.build();
}
}
Spring Secruity的配置,使用到的相关Handler在上文已经给出了
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Autowired
private LoginFailHandler loginFailHandler;
@Autowired
private LoginSuccessHandler loginSuccessHandler;
@Autowired
private MyAccessDeniedHandler accessDeniedHandler;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private LogoutSuccessHandler logoutSuccessHandler;
@Bean
public UserDetailsService userDetailsService() {
//使用hash加密
UserDetails user = User.withUsername("admin")
.password("{bcrypt}admin")
.roles("USER")
.build();
//使用bcrypt加密
UserDetails root = User.withUsername("root").password(bCryptPasswordEncoder().encode("123456"))
.roles("admin", "user").build();
//密码前面需要添加{id},id加密算法的名称,不然会报错,noop是不加密
UserDetails root1 = User.withUsername("root").password("{noop}123456")
.roles("admin", "user").build();
return new InMemoryUserDetailsManager(user, root, root1);
}
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
//表单提交
httpSecurity.formLogin(formLogin -> formLogin
//自定义登录页地址
.loginPage("/login.html")
.successHandler(loginSuccessHandler)
.failureHandler(loginFailHandler)
//登录接口
.loginProcessingUrl("/test/login")
//认证成功后跳转的路径
.defaultSuccessUrl("/index.html"));
//授权配置
httpSecurity.authorizeHttpRequests(authorize -> authorize
//设置哪些路径可以直接访问,不需认证
.requestMatchers("/login.html", "/test/login", "/swagger-ui/index.html").permitAll()
//其他的路径都需要认证
.anyRequest().authenticated());
//退出登录成功处理器
httpSecurity.logout(logout -> logout.logoutSuccessHandler(logoutSuccessHandler));
//授权失败处理配置
httpSecurity.exceptionHandling(exceptionHandling ->
exceptionHandling.accessDeniedHandler(accessDeniedHandler));
//禁用csrf
httpSecurity.csrf(csrf -> csrf.disable());
//设置自定义的UserDetailService,如果自定了UserDetailService,则需要设置这个
httpSecurity.userDetailsService(userDetailsService);
return httpSecurity.build();
}
}