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: SpringSecurityDemosecurity:user:name: admin #用户名password: 888888 #密码roles: #用户具有的角色- admin
使用配置类配置
@Beanpublic 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);}@Beanpublic BCryptPasswordEncoder bCryptPasswordEncoder() {return new BCryptPasswordEncoder();}
常用的Handler
SpirngSecurity提供了针对各种异常处理的Handler,比如登录错误处理Handler、登录成功处理Handler等,我们只需要继承这些Handler实现自定义的逻辑即可。
登录成功登录失败
登录失败处理逻辑
@Component
public class LoginFailHandler implements AuthenticationFailureHandler {@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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 {@Overridepublic 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需要配置后才能生效。
配置主要内容如下:
- 登录配置:设置自定义登录页、登录接口、登录成功失败处理逻辑以及认证成功后跳转路径
- 授权配置:主要是设置哪些接口不需要登录认证就能访问,哪些接口需要登录认证后才能访问
@Beanpublic 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));//禁用csrfhttpSecurity.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 {@Overridepublic 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 {@Autowiredprivate LoginFailHandler loginFailHandler;@Autowiredprivate LoginSuccessHandler loginSuccessHandler;@Autowiredprivate MyAccessDeniedHandler accessDeniedHandler;@Autowiredprivate UserDetailsService userDetailsService;@Autowiredprivate LogoutSuccessHandler logoutSuccessHandler;@Beanpublic 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);}@Beanpublic BCryptPasswordEncoder bCryptPasswordEncoder() {return new BCryptPasswordEncoder();}@Beanpublic 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));//禁用csrfhttpSecurity.csrf(csrf -> csrf.disable());//设置自定义的UserDetailService,如果自定了UserDetailService,则需要设置这个httpSecurity.userDetailsService(userDetailsService);return httpSecurity.build();}}
参考
- Spring Security官网
- SpringSecurity的 loginProcessingUrl为什么不能用
- spring security loginProcessingUrl无效问题