官网地址:spring.io/projects/spring-security
Spring Security是一个功能强大,可高度定制的认证与授权的框架;重点处理认证和授权2个功能
1.1)认证就是判断一个用户身份是否合法的过程,用户访问系统资源时系统要求验证用户的身份信息,根据认证的结果来进行后一步的操作;认证是为了保证系统的隐私数据与资源;
1.2)授权授权是用户通过认证后,根据用户的权益来控制用户可以访问资源的过程;授权是为了更细粒度地对隐私数据进行划分,授权是发生在认证通过之后;
1.3)会话系统为了保持当前用户的登录状态所提供的机制;常见的方式有session、token等方式;
二、入门示例 2.1)加入依赖<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> 复制代码2.2)启动类配置
//@EnableWebSecurity这个注解是SpringSecurity框架提供的注解,加入从注解后即集成了Security @EnableWebSecurity @SpringBootApplication public class SecurityDemoApplication { public static void main(String[] args) { SpringApplication.run(SecurityDemoApplication.class, args); } } 复制代码2.3)构建访问交互
@RestController @RequestMapping("/testDemo") public class TestDemoController { /** * 功能描述:测试访问1 * * @author : XXSD * @date : 2021/1/7 0007 上午 10:56 */ @RequestMapping("/demo1") public String demo1() { return "demo1"; } } 复制代码2.4)启动项目并访问
在浏览器输入: http://localhost:8081/testDemo/demo1
得到如下结果;注意:这里我们没有配置任何的页面,但是项目启动后输入上面的地址,会打开一个界面,这个界面是SpringSecurity为我们提供的一个默认界面;
在代码中并没有添加任何的登录页面,但是这里却出现了,显然是Security框架提供过的;
回顾控制台日志信息:
Connected to the target VM, address: '127.0.0.1:49458', transport: 'socket' . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.4.1) 2021-01-07 11:04:25.322 INFO 52984 --- [ main] xinyan.SecurityDemoApplication : Starting SecurityDemoApplication using Java 1.8.0_202 on JKY8986KLO9O81T with PID 52984 (F:\Tu_Ling\xuexi-demo\security-demo\target\classes started by Administrator in F:\Tu_Ling\xuexi-demo) 2021-01-07 11:04:25.327 INFO 52984 --- [ main] xinyan.SecurityDemoApplication : No active profile set, falling back to default profiles: default 2021-01-07 11:04:26.501 INFO 52984 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8081 (http) 2021-01-07 11:04:26.511 INFO 52984 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2021-01-07 11:04:26.511 INFO 52984 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.41] 2021-01-07 11:04:26.574 INFO 52984 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2021-01-07 11:04:26.574 INFO 52984 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1188 ms 2021-01-07 11:04:26.751 INFO 52984 --- [ main] .s.s.UserDetailsServiceAutoConfiguration : #在没有任何配置的情况下,Security会自动创建一个user用户,下面就自动生成的登录密码 Using generated security password: f82577c5-4c0f-420b-842c-34f837bf8c15 复制代码2.5)Security提供的默认配置
登录地址:【域名或IP】【:端口号】/login
登出地址:【域名或IP】【:端口号】/logout
启动类上添加:@EnableWebSecurity注解后自动集成Security;在什么都不进行配置的情况下会创建默认用户“user”;默认的密码在控制台打印;
三、认证授权逻辑实现结合第二节的示例继续
3.1)授权配置import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; /** * 类描述: Security授权配置 * <br /> * WebSecurityConfigurerAdapter是Sprng提供的一个适配器,通过这个适配器可以帮助开发对授权的行为进行配置 * 注意:需要加入@EnableWebSecurity注解,也可以使用@Configuration进行标注 * * @author XXSD * @version 1.0.0 * @date 2021/1/7 0007 上午 11:49 */ @EnableWebSecurity public class SecurityAuthorizationConfig extends WebSecurityConfigurerAdapter { /** * 在开启“记住我”功能的时候需要指定的一个UserDetailsService对象 */ private final UserDetailsService userDetailsService; public SecurityAuthorizationConfig(UserDetailsService userDetailsService){ this.userDetailsService=userDetailsService; } /** * 功能描述:超类中提供了3个confgure配置方法,这里使用Http请求访问规则权限类型的配置方法进行配置 * * @author : XXSD * @date : 2021/1/7 0007 下午 6:50 */ @Override protected void configure(HttpSecurity http) throws Exception { http //关闭跨域检查 .csrf().disable() .authorizeRequests() //配置路径及对应的资源权限 .antMatchers("/mobile/**").hasAuthority("mobile") .antMatchers("/salary/**").hasAuthority("salary") .antMatchers("/testDemo/**").hasAuthority("testDemo") //设置放行的请求 .antMatchers("/message/**").permitAll() //其他请求需要登录,注意在anyRequest()后就不能再配置规则了,因为既然已经全部放行了,后面再配置规则就没有意义了,同时也证明了这个配置是有先后顺序的; .anyRequest().authenticated() //设置为并行条件 .and() //开启“记住我”功能,开启后需要指定一个userDetailsService对象,同时也可以使用.rememberMeParameter("【自定义参数名称】")来指定参数的名称 .rememberMe().userDetailsService(userDetailsService) .and() //可以设置登录成功后跳转的页面 .formLogin() //配置自己的登录页,如果只配置了loginPage部分,没有配置loginProcessingUrl,那么会出现无法获取到用户的问题,因为提交的地址并没有真正的提交到SpringSecurity .loginPage("/myLogin.html").loginProcessingUrl("/login") .defaultSuccessUrl("/testDemo/demo1").failureUrl("/login"); } } 复制代码3.2)认证配置
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; /** * 类描述: Security认证配置 * * @author XXSD * @version 1.0.0 * @date 2021/1/7 0007 上午 11:49 */ @Configuration public class SecurityAuthenticationConfig implements WebMvcConfigurer { /** * 功能描述:注入自己的密码编码器 * * @author : XXSD * @date : 2021/1/7 0007 下午 12:02 */ @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(10); } /** * 功能描述:注册用户管理服务 * <br /> * 如果没有注入这个用户管理服务,那么在启动的时候Security框架会在UserDetailsServiceAutoConfiguration中帮你注入一个 * 用户名为“user”用户的InMemoryUserDetailsManager对象; * 另外也可以采用修改configure(AuthenticationManagerBuilder auth)方法注入AuthenticationManagerBean * <br /> * UserDetailsService是一个接口 * Spring不仅提供了InMemoryUserDetailsManager接口同时还提供了一个JdbcUserDetailsManager的实现类,专用于数据库的处理 * @author : XXSD * @date : 2021/1/7 0007 下午 5:14 */ @Bean public UserDetailsService userDetailsService() { return new InMemoryUserDetailsManager( /* * 这里直接创建了2个用户并赋予权限资源 * 在创建用户的时候要去必须指定一个资源 * passwordEncoder().encode("admin")是使用当前的加密器对密文进行加密 * */ User.withUsername("Admin").password(passwordEncoder().encode("admin")).authorities("testDemo","mobile", "salary").build(), //authorities配置中可以加入“ROLE_”前缀的资源名称 User.withUsername("manager").password(passwordEncoder().encode("manager")).authorities("mobile").build() ); } /** * 配置默认的跳转路径,这里可以定制自己的跳转url * Configure simple automated controllers pre-configured with the response * status code and/or a view to render the response body. This is useful in * cases where there is no need for custom controller logic -- e.g. render a * home page, perform simple site URL redirects, return a 404 status with * HTML content, a 204 with no content, and more. * * @param registry * @see ViewControllerRegistry */ @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/").setViewName("redirect:/login"); } } 复制代码四、Security拓展点 4.1)主体数据来源拓展点
SpringSecurity是通过引用Spring容器中的UserDetailsService对象来管理主体数据的,默认情况下,会注入一个包含用户信息的默认主体来管理服务;在实际的项目中用户信息都是来源于外部数据库,在SpringSecurity中提供了一个JdbcUserDetailsManager来实现对数据库的用户信息进行管理,当然这个自带的实现是基于大众化的,如果有个性化处理,可以自己实现一个UserDetailsService对象并注入到Spring容器中即可;
/** 自定义的UserDetailsService注入 具体实现方式参考JdbcUserDetailsManager */ @Bean public UserDetailsService userDetailsService() { return new 自定义的UserDetailsService 接口实现(); } 复制代码
UserDetails对象:
SpringSecurity中提供了一个UserDetails对象,此对象中有如下几个总要的属性
//密码 private String password; //用户名 private final String username; //权限 private final Set authorities; //账户是否超时 private final boolean accountNonExpired; //账户是否锁定 private final boolean accountNonLocked; //密码是否超时 private final boolean credentialsNonExpired; //是否失效 private final boolean enabled; 复制代码
以上accountNonExpired、accountNonLocked、credentialsNonExpired、enabled四个状态用来维护用户的可用性,当这4个状态全部为False的时候用户才可以正常登录;
4.2)密码解析器SpringSecurity提供了多个密码解析器,包括CryptPassEncoder、Argon2PasswordEncoder、Pbkdf2PasswordEncoder等,这些密码解析器都实现了PassEnger接口;目前最常用的是BCryptPasswordEncoder;值得注意的是,我们在选择不同的密码解析器后,后台存储用户密码的介质媒体内容也要发生更改;
BCryptPasswordEncoder加密器加密后的密文是不一样的,但是不管结果如何,都是可以被识别的(使用方式:new BCryptPasswordEncoder().matche(【密文原数据】, 【密文】));
BCryptPasswordEncoder是使用加严的方式进行的,即加密后的结果中“ ”是分隔符, ” 是 分 隔 符 , 之间是版本标识;最后其实就是密文+严内容,前22位是严,22位之后是密文加密后的数据;
4.3)自定义授权及安全拦截策略这个拓展点是整个SpringSecurity的核心部分;
在实际开发过程中,最常规的方式是通过覆盖WebSecurityConfigurerAdapter中的protected void configure(HttpSecurity http)方法;通过Http来配置自定义的拦截规则,包含访问控制、界面及逻辑、退出页面及逻辑等;
http.loginPage()方法配置登录页,http.loginProcesingUrl()方法定制登录逻辑;这里需要特别注意的是:SpringSecurity的登录页和逻辑hi同一个“/login”地址,如果使用自定义的页面,需要将登录逻辑地址进行分离;
http.loginPage("/index.html").loginProcessingUrl("/login"); 复制代码
具体的登录页面实现的逻辑,请参考系统提供的默认登录页,登录页面的源码可以在DefaultLoginPageGeneratingFilter中找到,需要注意的是登录页面的相关访问权限;
SpringSecurity本质上是前后端不分离的;
登录页面提供了记住我功能,此功能只需往登录接口提交一个remeber-me参数即可,其值可选范围:no、yes、1、true;就会记住当前登录用户的Token到Cookie中;
http.rememberMe().rememberMeParameter("remeber-me") 复制代码
这个配置参数在登出时,会清除记住我功能的cookie;
antMachers()方法设置路径的匹配逻辑,可以使用通配符进行配置,两个星号来标识多层路径,一个星号代表一个或多个字符,问号代表一个字符;
permitAll():所有人都可以访问;
denyAll():所有人都不能访问;
anonymous():只有未登录的人可以访问,已经登录的人无法访问;
hasAuthority、hasRole这些是配置需要有对应的权限或者角色才能访问的控制逻辑方法;其中角色就是对应一个“ROLE_【角色名】”这样的资源;
AuthenticationManagerBuilde:配置认证谋略;
WebSecurity:配置补充的Web请求策略;
是Cross-Site Request Forgery的缩写,意义为:跨站点请求伪造,这是一种***手段;
SpringSecurity针对CSRF是由一整套专门的检查机制;在后台session中加入一个csif的Token值,然后向后端发起请求,对于Get、Head、Trace、Options以外的请求(例如:POST、PUT、DELETE等),会要求带上这个Token值进行对比;
在SpringSecurity有一个CsrfFilter专门负责对Csrf参数进行检查,会调用HttpSessionCsrfTokenRepository生成一个CsrfToken,并将其保存到Session中;
在启动类上添加@EnableGlobalMethodsecurity注解即可开启;
prePostEnabled:是否开启对@PreAuthorize注解的支持;
securedEnabled:是否开启对@Secured注解的支持,角色级别的控制;
jsr250Enabled:是否开启对@RolesAllowed注解的支持,等价于@Secured;
前后端分离的情况下可以使用@ControllerAdvice注入一个异常处理类,以@ExceptionHandler注解声明方法,往前端推送异常信息;
@ControllerAdvice public class SecurityExceptionManager { /** * 功能描述:处理没有权限的情况 * @author : XXSD * @date : 2021/1/8 0008 下午 4:17 */ @ResponseBody @ExceptionHandler(AccessDeniedException.class) public String exceptionManager(){ return ""; } } 复制代码
原文链接:https://juejin.cn/post/6921907719121305614
如果觉得本文对你有帮助,可以关注一下我公众号,回复关键字【面试】即可得到一份Java核心知识点整理与一份面试大礼包!另有更多技术干货文章以及相关资料共享,大家一起学习进步!