环境:SpringBoot2.7.12 Spring Security是基于架构建一个功能强大且高度可定制的身份验证和访问控制框架
,用于保护基于Spring的份验应用程序。它采用AOP思想,证授基于servlet过滤器实现安全框架
。权框 Spring Security具有以下优势: 本篇文章将会介绍常用的配置及相应的扩展点
。 在Spring Security5.7之前版本通过继承WebSecurityConfigurerAdapter类 5.7之后版本 在这里每定义一个SecurityFilterChain所注入的源码下载HttpSecurity都是唯一的实例对象
。 后续所有的配置都是基于Spring Security5.7.8版本 通过上面自定义认证器可以实现自己的验证逻辑。 通过自定义UserDetailsService也可以实现对应的逻辑,只不过这种方式你还需要提供一个PasswordEncoder 注意:你的UserDetails必须重写equals和hashCode方法 总结:以上是在实际开发中经常会应用到的一些配置及相应功能的免费模板使用。Spring Security是一个强大的安全认证框架,它提供了丰富的安全功能来保护您的应用程序。通过本文的基础配置示例,你可以轻松地开始使用Spring Security来保护你的应用程序并实现身份验证和授权功能。模板下载 完毕!
! !1. 简介
2. 实战案例
2.1 自定义配置
2.2 自定义验证器
复制@Component public class MemeryAuthticationProvider implements AuthenticationProvider { @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication ; Object principal = token.getPrincipal() ; Object credentials = token.getCredentials() ; User user = users.get(principal) ; // notNull(user, "用户名或密码错误") ; if (user == null) { return null ; } if (!user.getPassword().equals(credentials)) { throw new RuntimeException("密码错误") ; } return new UsernamePasswordAuthenticationToken(principal, credentials, user.getAuthorities()) ; } @Override public boolean supports(Class<?> authentication) { return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication) ; } }1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29. 2.2 自定义UserDetailsService
2.3 拦截指定路径的请求
复制@Bean public SecurityFilterChain apiSecurityFilterChain(HttpSecurity http) throws Exception { http.csrf().disable() ; // 该过滤器链 ,高防服务器只匹配/api/**路径的请求 http.requestMatcher(new AntPathRequestMatcher("/api/**")) ; // 也可以这样配置多个 // http.requestMatchers().antMatchers("/api/**", "/admin/**") ; // ... DefaultSecurityFilterChain chain = http.build(); return chain ; }1.2.3.4.5.6.7.8.9.10.11. 2.4 拦截指定路径及权限
复制@Bean public SecurityFilterChain apiSecurityFilterChain(HttpSecurity http) throws Exception { http.csrf().disable() ; http.authorizeHttpRequests().requestMatchers(new AntPathRequestMatcher("/api/save")).hasAnyRole("C") ; http.authorizeHttpRequests().requestMatchers(new AntPathRequestMatcher("/api/find")).hasAuthority("ROLE_U") ; DefaultSecurityFilterChain chain = http.build(); return chain ; }1.2.3.4.5.6.7.8. 2.5 自定义授权决定
复制http.authorizeHttpRequests(registry -> { registry.antMatchers("/api/{ id}").access(new AuthorizationManager<RequestAuthorizationContext>() { @Override public AuthorizationDecision check(Supplier<Authentication> authentication, RequestAuthorizationContext object) { Map<String, String> variables = object.getVariables() ; // 返回的路径是/api/666则进行拦截并且指定具有D的权限 return new AuthorityAuthorizationDecision(variables.get("id").equals("666"), Arrays.asList(new SimpleGrantedAuthority("D"))) ; } }) ; }) ;1.2.3.4.5.6.7.8.9.10.11. 2.6 自定义异常处理
复制http.exceptionHandling(customizer -> { customizer.accessDeniedHandler(new AccessDeniedHandler() { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { Map<String, Object> errors = new HashMap<>() ; response.setContentType("application/json;charset=utf-8") ; errors.put("code", -1) ; errors.put("status", response.getStatus()) ; errors.put("message", accessDeniedException.getMessage()) ; errors.put("details", ExceptionUtils.getMessage(accessDeniedException)) ; response.getWriter().println(new ObjectMapper().writeValueAsString(errors)) ; } }) ; }) ;1.2.3.4.5.6.7.8.9.10.11.12.13.14.15. 2.7 自定义角色继承
复制@Bean public RoleHierarchy hierarchyVoter() { RoleHierarchyImpl hierarchy = new RoleHierarchyImpl(); // ADMIN自动拥有MANAGER的权限 hierarchy.setHierarchy("ROLE_ADMIN > ROLE_MANAGER"); return hierarchy ; }1.2.3.4.5.6.7. 2.8 自定义退出登录逻辑
复制http.logout().logoutUrl("/logout").addLogoutHandler(new LogoutHandler() { @Override public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { System.out.println("退出登录") ; } }).logoutSuccessHandler(new LogoutSuccessHandler() { @Override public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { response.setContentType("text/html;charset=utf-8"); PrintWriter out = response.getWriter() ; out.println("<h2>退出登录成功</h2>") ; out.close() ; } }) ;1.2.3.4.5.6.7.8.9.10.11.12.13.14.15. 2.9 自定义登录失败逻辑
复制http .formLogin() .failureHandler(new AuthenticationFailureHandler() { @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception response.setContentType("application/json;charset=UTF-8") ; PrintWriter out = response.getWriter() ; out.println("{ \"code\": -1, \"message\": \"" + getRootCause(exception).getMessage() + "\"}") ; out.close(); } });1.2.3.4.5.6.7.8.9.10.11. 2.10 自定义过滤器
复制@Bean public PackAuthenticationFilter packAuthenticationFilter() { return new PackAuthenticationFilter() ; } // 添加自定义过滤器到Security Filter Chain中 http.addFilterBefore(packAuthenticationFilter(), RequestCacheAwareFilter.class) ;1.2.3.4.5.6. 2.11 配置多个过滤器链
复制@Bean public SecurityFilterChain apiSecurityFilterChain(HttpSecurity http) throws Exception { // 拦截/api/** http.requestMatcher(new AntPathRequestMatcher("/api/**")) ; } @Bean public SecurityFilterChain apiSecurityFilterChain(HttpSecurity http) throws Exception { // 拦截/admin/** http.requestMatcher(new AntPathRequestMatcher("/admin/**")) ; }1.2.3.4.5.6.7.8.9.10. 2.12 开启全局方法拦截
复制@Configuration @EnableGlobalMethodSecurity(jsr250Enabled = true, prePostEnabled = true, securedEnabled = true) public class SecurityConfig { } // 使用 @GetMapping("/find") @PreAuthorize("hasRole(GUEST)") public Object find(HttpServletResponse response) throws Exception { return "find method invoke..." ; }1.2.3.4.5.6.7.8.9. 2.13 国际化支持
复制@Bean public ReloadableResourceBundleMessageSource messageSource() { ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); // 这里会按照顺序查找的 messageSource.addBasenames( "classpath:org/springframework/security/messages", "classpath:messages/messages" ) ; return messageSource ; }1.2.3.4.5.6.7.8.9.10. 2.14 防止重复登录
复制http.sessionManagement().maximumSessions(1).expiredSessionStrategy(new SessionInformationExpiredStrategy() { @Override public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException { HttpServletResponse response = event.getResponse() ; response.setContentType("application/json;charset=UTF-8"); PrintWriter out = response.getWriter(); out.println("{ \"code\": -1, \"message\": \"会话已过期,或重复登录\"}"); out.close(); } }) ;1.2.3.4.5.6.7.8.9.10.