建设银行网站注销吗,做ppt会去什么网站找图,个人介绍网页制作,厦门网站设计公司找哪家厦门小程序建设背景#xff1a; 前两篇文章将spring-security的设计架构、核心类、配置及构建过程基本过了一遍#xff0c;其实很偏理论#xff0c;如果对源码不感兴趣或项目使用不深#xff0c;基本可以忽略#xff0c;毕竟完全理解可能也不会用到#xff0c;时间长也忘掉了。但是如果…背景 前两篇文章将spring-security的设计架构、核心类、配置及构建过程基本过了一遍其实很偏理论如果对源码不感兴趣或项目使用不深基本可以忽略毕竟完全理解可能也不会用到时间长也忘掉了。但是如果你想对代码进行微调或者写出自己想要的设计效果那么读一读还是很有必要毕竟开发过程就是一个学习的过程。本篇是在参考遍地继承WebSecurityConfigurerAdapter的方案上再加上自身阅读源码后的理解结合自身需求而尝试出来的。 Spring-Security全局导读 1、Security核心类设计 2、HttpSecurity结构和执行流程解读 3、Spring-Security个人落地篇 ps1WebSecurityConfigurerAdapter在较新的版本中都已经被标注过期了这也是我读源码时尝试自己尝试新方案的动机之一 ps2 落地方案其实和最新官方推荐的方案很接近因为我也是从自动配置类中的SecurityFilterChain的创建过程而猜想过来的 ps3强烈建议阅读松哥之前写的security系列文章看看这一篇就知道他的水平了 ps4如果有时间有想法自己写一些试试很久以前粗略看过松哥Security系列的一部分文章当时觉得自己懂了不知过了多久已经完全忘记基础概念了。
废话过多以下为个人输出简略篇 一、POM依赖
?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersionparentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.7.14/versionrelativePath/ /parentgroupId/groupIdartifactId/artifactIdversion/versionname/namedescription/descriptionpropertiesjava.version/java.version/propertiesdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-security/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-validation/artifactId/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdscopecompile/scope/dependency/dependenciesbuildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactId/plugin/plugins/build
/project二、自定义拦截及授权封装类
/*** 1、CustomAuthenticationProvider等其它内部类不能设置为静态类* 2、CustomUsernamePasswordAuthenticationFilter必须重写afterPropertiesSet方法并忽略管理器校验*/
Component
ConditionalOnProperty(value password.enable, havingValue true, matchIfMissing true)
public class CustomAuthenticationContext {Componentprivate class CustomAuthenticationProvider implements AuthenticationProvider {Overridepublic Authentication authenticate(Authentication authentication) throws AuthenticationException {UsernamePasswordAuthenticationToken token (UsernamePasswordAuthenticationToken) authentication;if (test.equals(token.getPrincipal().toString()) test.equals(token.getCredentials().toString())) {return UsernamePasswordAuthenticationToken.authenticated(test, null, null);}throw new BadCredentialsException(账号信息错误);}Overridepublic boolean supports(Class? authentication) {return UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication);}}Componentprivate class CustomUsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {private final Validator validator Validation.buildDefaultValidatorFactory().getValidator();private ObjectMapper objectMapper;public CustomUsernamePasswordAuthenticationFilter() {super(new AntPathRequestMatcher(/user/login, POST));}Overridepublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {InputStream content request.getInputStream();User user objectMapper.readValue(content, User.class);SetConstraintViolationUser validate validator.validate(user, Default.class);if (validate.isEmpty()) {UsernamePasswordAuthenticationToken authRequest UsernamePasswordAuthenticationToken.unauthenticated(user.getName(), user.getPassword());authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));return this.getAuthenticationManager().authenticate(authRequest);}throw new BadCredentialsException(用户名或密码错误);}Autowiredvoid init(CustomAuthResult authResult, ObjectMapper objectMapper) {this.objectMapper objectMapper;setAuthenticationSuccessHandler(authResult);setAuthenticationFailureHandler(authResult);}Override// 后期统一设置未重写则实例创建后校验报错public void afterPropertiesSet() {}}Dataprivate static class User {NotBlankprivate String name;NotBlankprivate String password;} }说明 1、自定义鉴权中Filter及AuthenticationProvider通常是一对一的此处封装为一个类并用条件注解修饰避免多场景下的鉴权组合造成的代码混乱虽然此类场景通常不会有太大的变更 2、此块逻辑其实可以放到controller中但注意授权上下文的控制及HttpSecurity的设置
三、Security框架配置入口类
Configuration
public class SecurityFilterChainConfiguration {Autowired// HttpSecurity默认是多例的此处如果多个方法调用必须唯一private HttpSecurity httpSecurity;// 参数列表可通过自定义类上条件注解控制// 单个类中同时存在多个Autowired执行顺序不固定// 这两个类是一体的可以封装在一起不用搞得太零散Autowiredvoid authenticationManager(ListAuthenticationProvider customProviders, ListAbstractAuthenticationProcessingFilter customProcessingFilter) {// 自封装授权管理器没有使用系统AuthenticationManagerBuilder创建的管理器AuthenticationManager authenticationManager new ProviderManager(customProviders);for (AbstractAuthenticationProcessingFilter filter : customProcessingFilter) {filter.setAuthenticationManager(authenticationManager);// UsernamePasswordAuthenticationFilter.class为系统内置FilterhttpSecurity.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class);}}BeanSecurityFilterChain securityFilterChain(CustomAuthResult authResult) throws Exception {/** 此块HttpSecurity链式配置包含三块内容* 1、csrf会在response中添加token header以避免非原始请求客户端伪造访问* 2、关闭默认的表单登录所有鉴权逻辑自定义* 3、针对logout、异常等场景细节进行配置*/httpSecurity.csrf().disable()// 如果自定义授权了表单登录可以关闭通常用于前后台分离项目.formLogin().disable().authorizeRequests().antMatchers(HttpMethod.OPTIONS).permitAll()// 这块是为在controller中做授权校验而放开的此场景后续补充.antMatchers(HttpMethod.POST, /doLogin).permitAll().anyRequest().authenticated().and().logout().logoutSuccessHandler(authResult).permitAll()// 设置访问未授权资源的处理器.and().exceptionHandling().authenticationEntryPoint(authResult);// 手工build没有借助WebSecurity类的管理return httpSecurity.build();}
}四、响应封装合体类
Component
public class CustomAuthResult implements AuthenticationFailureHandler, AuthenticationSuccessHandler, AuthenticationEntryPoint, LogoutSuccessHandler {Overridepublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {response.setContentType(application/json;charsetutf-8);response.setStatus(HttpStatus.UNAUTHORIZED.value());response.getWriter().write({\code\:\1004\,\msg\:\用户名或密码错误\});response.getWriter().flush();response.getWriter().close();}Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {response.setContentType(application/json;charsetutf-8);response.getWriter().write({\code\:\1000\,\msg\:\登录成功\});response.getWriter().flush();response.getWriter().close();}Overridepublic void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {response.setContentType(application/json;charsetutf-8);response.setStatus(HttpStatus.FORBIDDEN.value());response.getWriter().write({\code\:\1001\,\msg\:\用户未登录\});response.getWriter().flush();response.getWriter().close();}Overridepublic void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {response.setContentType(application/json;charsetutf-8);response.getWriter().write({\code\:\1003\,\msg\:\成功退出\});response.getWriter().flush();response.getWriter().close();}
}说明此类自己调试随便建的生产项目需要有严格而准确的封装和处理
简单自定义使用以上就足够了相关注意事项也在注释中说明不管是继承WebSecurityConfigurerAdapter的方式还是其他方式核心类的执行流程其实是不变的变的只是配置方式或组合方式的外皮所以把基础概念了解清楚了不管是spring5还是spring6的版本变化基本不会有太大的挑战。
PS 1、关于用户权限数据管理逻辑框架层提供了UserDetailsService接口及对应内存数据库的默认实现这个需求差异较大如验证码登录此处不再展开直接在验证逻辑处写死配置。 2、在新版本中参考依赖不管是继承WebSecurityConfigurerAdapter的方式还是哪种方式都不需要再自行使用EnableWebSecurity注解在类上进行标注了自动配置类中已告知。
附Adapter模式简化配置版
Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {Autowired// 代码参考上述方案定义private CustomAuthResult authResult;Autowired// AuthenticationManager配置类复用全局AuthenticationManagerprivate AuthenticationConfiguration configuration;Override// 适配器模式只需要重写此方法完成内部过滤器链配置protected void configure(HttpSecurity http) throws Exception {// 自定义安全管理过滤器对应鉴权和参数封装逻辑此处忽略http.addFilterBefore(adaptUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);http.csrf().disable().formLogin().disable().authorizeRequests().antMatchers(HttpMethod.OPTIONS).permitAll().anyRequest().authenticated().and().logout().logoutSuccessHandler(authResult).permitAll().and().exceptionHandling().authenticationEntryPoint(authResult);}Bean// 自定义Filter内部有其他Autowired注解需要处理所以需要发布到容器// 如果手工set可以考虑不发布到spring容器中限制其作用域范围AdaptUsernamePasswordAuthenticationFilter adaptUsernamePasswordAuthenticationFilter() throws Exception {AuthenticationManager authenticationManager configuration.getAuthenticationManager();AdaptUsernamePasswordAuthenticationFilter adaptUsernamePasswordAuthenticationFilter new AdaptUsernamePasswordAuthenticationFilter(/user/login);adaptUsernamePasswordAuthenticationFilter.setAuthenticationManager(authenticationManager);return adaptUsernamePasswordAuthenticationFilter;}Bean// 自定义授权管理器代码和上述方案定义类一致但上述是内部类包路径不一致public AuthenticationProvider authenticationProvider() {return new CustomAuthenticationProvider();}
}可以看到继承WebSecurity适配器的代码同样也很简单核心还是HttpSecurity的构建不过第一种方案灵活性更高我们自己组织和封装的可能性更好体现模块化的思想适配方式已经标注过期最新版本已经删除可作参考。