首页>>后端>>SpringBoot->SpringBoot 集成 Security

SpringBoot 集成 Security

时间:2023-11-30 本站 点击:1

Spring Security 介绍

Spring Security 是基于 Spring 框架的权限管理框架

Spring Security 的前身是 Acegi Security

Acegi Security 以配置繁琐而被诟病,投入 Spring 怀抱后,随着 SpringBoot 的崛起,Spring Security 的易用性得到了极大的提升,经常被用于 SpringBoot 及 SpringCloud 项目

Spring Security 的基本功能

认证:提供多种常见的认证方式

授权:提供基于 URL 的请求授权、支持方法访问授权以及对象访问授权

基本原理

Spring Security 是通过一层层 Filter 来处理 web 请求的

在 Filter 组成的链条中,逐步完成认证和授权,发现异常则抛给异常处理器处理

过滤器链中的核心概念

springSecurityFilterChain

Spring Security 的核心过滤器叫 springSecurityFilterChain,类型是 FilterChainProxy

WebSecurity、HttpSecurity

WebSecurity 构建了 FilterChainProxy 对象

HttpSecurity 构建了 FilterChainProxy 中的一个 SecurityFilterChain

WebSecurityConfiguration

@EnableWebSecurity 注解,导入了 WebSecurityConfiguration 类

WebSecurityConfiguration 中创建了建造者对象 WebSecurity 和核心过滤器 FilterChainProxy

Spring Security 常用组件

Authentication:认证接口,定义了认证对象的数据形式。

AuthenticationManager:用于校验 Authentication,返回一个认证完成后的

SecurityContext:上下文对象,用来存储 Authentication

SecurityContextHolder:用来访问 SecurityContext

GrantedAuthority:代表权限

UserDetails:代表用户信息

UserDetailsService:获取用户信息

简单使用

引入 Spring Security 依赖

<!--引入SpringSecurity--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId></dependency>

引入依赖后,不做任何配置,Spring Security 会自动生效,请求将跳转登录页面

默认用户名、密码和权限可在 application.yaml 中配置

spring:security:user:name:mingpassword:123456roles:admin

基于内存的认证

@Configuration@EnableWebSecurity//开启注解设置权限@EnableGlobalMethodSecurity(prePostEnabled=true)publicclassWebSecurityConfigextendsWebSecurityConfigurerAdapter{//配置密码加密器@BeanpublicPasswordEncoderpasswordEncoder(){returnnewBCryptPasswordEncoder();}//配置认证管理器@Overrideprotectedvoidconfigure(AuthenticationManagerBuilderauth)throwsException{auth.inMemoryAuthentication().withUser("admin").password(passwordEncoder().encode("123")).roles("admin").and().withUser("user").password(passwordEncoder().encode("456")).roles("user");}//配置安全策略@Overrideprotectedvoidconfigure(HttpSecurityhttp)throwsException{//设置路径及要求的权限,支持ant风格路径写法http.authorizeRequests()//设置OPTIONS尝试请求直接通过.antMatchers(HttpMethod.OPTIONS,"/**").permitAll().antMatchers("/api/demo/user").hasAnyRole("user","admin")//注意使用hasAnyAuthority角色需要以ROLE_开头.antMatchers("/api/demo/admin").hasAnyAuthority("ROLE_admin").antMatchers("/api/demo/hello").permitAll().and()//开启表单登录.formLogin().permitAll().and()//开启注销.logout().permitAll();}}

前后端分离

关闭 CSRF 防御和会话管理

CSRF 防御要求表单登录时携带 CSRF Token,前后端分离时不需要开启

会话管理设置为 STATELESS,使用无状态的 JWT 进行鉴权

@Overrideprotectedvoidconfigure(HttpSecurityhttp)throwsException{//关闭csrf防御http.csrf().disable();//关闭会话管理http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);//...}

自定义登录逻辑

Spring Security 默认使用表单登录,若要支持 JSON 请求,可继承UsernamePasswordAnthenticationFilter,并使用HttpSecurityaddFilterAt替换原有

publicclassCustomAuthenticationFilterextendsUsernamePasswordAuthenticationFilter{@OverridepublicAuthenticationattemptAuthentication(HttpServletRequestrequest,HttpServletResponseresponse)throwsAuthenticationException{//判断是否为JSON格式请求if(request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)){//...}else{returnsuper.attemptAuthentication(request,response);}}}

通过配置 AuthenticationManagerBuilder,设置自定义的 UserDetailsService

@AutowiredprivateCustomUserDetailsServicecustomUserDetailsService@Overrideprotectedvoidconfigure(AuthenticationManagerBuilderauth)throwsException{auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder());}

实现 UserDetailsService 的 loadUserByUsername 方法

publicclassCustomUserDetailsServiceimplementsUserDetailsService{@OverridepublicUserDetailsloadUserByUsername(Strings)throwsUsernameNotFoundException{//根据username查询用户Useruser=userMapper.getUserByUsername(s);if(user==null){//...}//查询角色或权限List<SimpleGrantedAuthority>authorities=userMapper.listRolesByUsername(s).stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());//构造UserDetails实例并返回}}

自定义登录成功处理器

通过配置 HttpSecurity,设置自定义的 successHandler

@Overrideprotectedvoidconfigure(HttpSecurityhttp)throwsException{http.formLogin().permitAll().loginProcessingUrl("/login").successHandler(customLoginSuccessHandler)}

CustomLoginSuccessHandler,以 JSON 形式返回前端,携带生成的 Token

@Component@RequiredArgsConstructorpublicclassCustomLoginSuccessHandlerimplementsAuthenticationSuccessHandler{privatefinalJwtUtiljwtUtil;@OverridepublicvoidonAuthenticationSuccess(HttpServletRequestrequest,HttpServletResponseresponse,Authenticationauthentication)throwsIOException{//构造一个统一返回格式对象Map<String,Object>res=newHashMap<>();res.put("code",200);res.put("message":"认证成功");res.put("path":"login");Objectprincipal=authentication.getPrincipal();if(principalinstanceofUser){//根据用户信息,使用JWT工具类构建Token//...//存到返回内容中res.put("data","xxxxxx")}//以JSON格式写入responseresponse.setContentType(MediaType.APPLICATION_JSON_VALUE);response.setCharacterEncoding("UTF-8");PrintWriterwriter=response.getWriter();writer.print(JsonUtil.Obj2Str(res));writer.flush();}}

自定义登录失败处理器

通过配置 HttpSecurity,设置自定义的 failureHandler

@Overrideprotectedvoidconfigure(HttpSecurityhttp)throwsException{http.formLogin().permitAll().loginProcessingUrl("/login").failureHandler(customLoginFailureHandler)}

CustomLoginFailureHandler,返回认证失败和失败信息

spring:security:user:name:mingpassword:123456roles:admin0

自定义未登录处理器

配置 authenticationEntryPoint

spring:security:user:name:mingpassword:123456roles:admin1

CustomAuthenticationEntryPoint

spring:security:user:name:mingpassword:123456roles:admin2

自定义权限不足处理器

配置 accessDeniedHandler

spring:security:user:name:mingpassword:123456roles:admin3

CustomAccessDeniedHandler

spring:security:user:name:mingpassword:123456roles:admin4

自定义注销成功逻辑

配置 logoutSuccessHandler

spring:security:user:name:mingpassword:123456roles:admin5

CustomLogoutSuccessHandler

spring:security:user:name:mingpassword:123456roles:admin6

也可以使用 HttpSecurity 的 addLogoutHandler,配置注销的处理逻辑

自定义 JWT 过滤器

添加 JWT 过滤器到过滤器链

spring:security:user:name:mingpassword:123456roles:admin7

JwtAuthenticationTokenFilter

spring:security:user:name:mingpassword:123456roles:admin8

动态配置 URL 权限

Spring Security 的过滤器链中包含了许多过滤器,其中 FilterSecurityInterceptor 非常重要,完成了主要的鉴权逻辑

beforeInvocation 方法

attemptAuthorization

从源码可以看出,动态配置 URL 权限有两种途径

自定义 SecurityMetadataSource,从数据源加载 ConfigAttribute

spring:security:user:name:mingpassword:123456roles:admin9

由于 SecurityConfig.createList 返回的是 SecurityConfig 类型的 ConfigAttribute,默认使用的 WebExpressionVoter 投票器用于验证 WebExpressionConfigAttribute 类型,因此还需要配置一个 RoleVoter

WebExpressionConfigAttribute 是指在配置类中通过 HttpSecurity 配置的权限

配置 HttpSecurity

@Configuration@EnableWebSecurity//开启注解设置权限@EnableGlobalMethodSecurity(prePostEnabled=true)publicclassWebSecurityConfigextendsWebSecurityConfigurerAdapter{//配置密码加密器@BeanpublicPasswordEncoderpasswordEncoder(){returnnewBCryptPasswordEncoder();}//配置认证管理器@Overrideprotectedvoidconfigure(AuthenticationManagerBuilderauth)throwsException{auth.inMemoryAuthentication().withUser("admin").password(passwordEncoder().encode("123")).roles("admin").and().withUser("user").password(passwordEncoder().encode("456")).roles("user");}//配置安全策略@Overrideprotectedvoidconfigure(HttpSecurityhttp)throwsException{//设置路径及要求的权限,支持ant风格路径写法http.authorizeRequests()//设置OPTIONS尝试请求直接通过.antMatchers(HttpMethod.OPTIONS,"/**").permitAll().antMatchers("/api/demo/user").hasAnyRole("user","admin")//注意使用hasAnyAuthority角色需要以ROLE_开头.antMatchers("/api/demo/admin").hasAnyAuthority("ROLE_admin").antMatchers("/api/demo/hello").permitAll().and()//开启表单登录.formLogin().permitAll().and()//开启注销.logout().permitAll();}}0

自定义一个投票器,在投票器中可以获取 URL,动态加载权限,可参考 RoleVoter

@Configuration@EnableWebSecurity//开启注解设置权限@EnableGlobalMethodSecurity(prePostEnabled=true)publicclassWebSecurityConfigextendsWebSecurityConfigurerAdapter{//配置密码加密器@BeanpublicPasswordEncoderpasswordEncoder(){returnnewBCryptPasswordEncoder();}//配置认证管理器@Overrideprotectedvoidconfigure(AuthenticationManagerBuilderauth)throwsException{auth.inMemoryAuthentication().withUser("admin").password(passwordEncoder().encode("123")).roles("admin").and().withUser("user").password(passwordEncoder().encode("456")).roles("user");}//配置安全策略@Overrideprotectedvoidconfigure(HttpSecurityhttp)throwsException{//设置路径及要求的权限,支持ant风格路径写法http.authorizeRequests()//设置OPTIONS尝试请求直接通过.antMatchers(HttpMethod.OPTIONS,"/**").permitAll().antMatchers("/api/demo/user").hasAnyRole("user","admin")//注意使用hasAnyAuthority角色需要以ROLE_开头.antMatchers("/api/demo/admin").hasAnyAuthority("ROLE_admin").antMatchers("/api/demo/hello").permitAll().and()//开启表单登录.formLogin().permitAll().and()//开启注销.logout().permitAll();}}1

配置 HttpSecurity

@Configuration@EnableWebSecurity//开启注解设置权限@EnableGlobalMethodSecurity(prePostEnabled=true)publicclassWebSecurityConfigextendsWebSecurityConfigurerAdapter{//配置密码加密器@BeanpublicPasswordEncoderpasswordEncoder(){returnnewBCryptPasswordEncoder();}//配置认证管理器@Overrideprotectedvoidconfigure(AuthenticationManagerBuilderauth)throwsException{auth.inMemoryAuthentication().withUser("admin").password(passwordEncoder().encode("123")).roles("admin").and().withUser("user").password(passwordEncoder().encode("456")).roles("user");}//配置安全策略@Overrideprotectedvoidconfigure(HttpSecurityhttp)throwsException{//设置路径及要求的权限,支持ant风格路径写法http.authorizeRequests()//设置OPTIONS尝试请求直接通过.antMatchers(HttpMethod.OPTIONS,"/**").permitAll().antMatchers("/api/demo/user").hasAnyRole("user","admin")//注意使用hasAnyAuthority角色需要以ROLE_开头.antMatchers("/api/demo/admin").hasAnyAuthority("ROLE_admin").antMatchers("/api/demo/hello").permitAll().and()//开启表单登录.formLogin().permitAll().and()//开启注销.logout().permitAll();}}2


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:/SpringBoot/4431.html