Spring Boot针对Spring Security提供了自动化配置方案,因此可以使SpringSecurity非常容易地整合进Spring Boot项目中,这也是在Spring Boot项目中使用Spring Security的优势。
添加依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
默认的用户名是user,默认的登录密码则在每次启动项目时随机生成,查看项目启动日志
配置用户名和密码:
可以在application.properties中配置默认的用户名、密码以及用户角色,配置方式如下
spring:
security:
user:
name: xc
password: 123456
roles: admin
当开发者在application.properties中配置了默认的用户名和密码后,再次启动项目,项目启动日志就不会打印出随机生成的密码了,用户可直接使用配置好的用户名和密码登录
基于内存的认证:
开发者也可以自定义类继承自WebSecurityConfigurerAdapter,进而实现对Spring Security更多的自定义配置,例如基于内存的认证,配置方式如下:
@Configuration public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter { @Bean PasswordEncoder passwordEncoder() { return NoOpPasswordEncoder.getInstance(); } // 配置用户 @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("root").password("123").roles("ADMIN", "DBA") .and() .withUser("admin").password("123").roles("ADMIN", "USER") .and() .withUser("sang").password("123").roles("USER"); } // 配置资源 @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests()// 调用authorizeRequests()方法开启HttpSecurity的配置 .antMatchers("/admin/**").hasRole("ADMIN")//表示用户访问“/admin/**”模式的URL必须具备ADMIN的角色 .antMatchers("/user/**").access("hasAnyRole('ADMIN','USER')") .antMatchers("/db/**").access("hasRole('ADMIN') and hasRole('DBA')") .anyRequest().authenticated()//表示除了前面定义的URL模式之外,用户访问其他的URL都必须认证后访问(登录后访问) .and() .formLogin()// 表示开启表单登录 .loginPage("/login_page")//这个login_page就是开发者自定义的登录页面,而不再是Spring Security提供的默认登录页。 .loginProcessingUrl("/login")//配置了登录接口为“/login”,配置loginProcessingUrl接口主要是方便Ajax或者移动端调用登录接口 .usernameParameter("name")//登录参数中用户名默认命名为username .passwordParameter("passwd")//密码默认命名为password .successHandler((req, resp, auth) -> { /* * 定义了登录成功的处理逻辑。用户登录成功后可以跳转到某一个页面,也可以返回一段JSON,这个要看具体业务逻辑, * 本案例假设是第二种,用户登录成功后,返回一段登录成功的JSON。 * onAuthenticationSuccess方法的第三个参数Authentication一般用来获取当前登录用户的信息, * 在登录成功后,可以获取当前登录用户的信息一起返回给客户端。 */ resp.setContentType("application/json;charset=utf-8"); resp.setStatus(200); Map<String, Object> map = new HashMap<>(); map.put("status", 200); map.put("msg", auth.getPrincipal()); PrintWriter out = resp.getWriter(); out.write(new ObjectMapper().writeValueAsString(map)); out.flush(); out.close(); }) .failureHandler((req, resp, e) -> { /* * 定义了登录失败的处理逻辑,和登录成功类似,不同的是,登录失败的回调方法里有一个AuthenticationException参数, * 通过这个异常参数可以获取登录失败的原因,进而给用户一个明确的提示。 */ resp.setContentType("application/json;charset=utf-8"); resp.setStatus(401); Map<String, Object> map = new HashMap<>(); map.put("status", 401); if (e instanceof LockedException) { map.put("msg", "账户被锁定,登录失败!"); } else if (e instanceof BadCredentialsException) { map.put("msg", "账户名或密码输入错误,登录失败!" + e.getMessage()); } else if (e instanceof DisabledException) { map.put("msg", "账户被禁用,登录失败!"); } else if (e instanceof AccountExpiredException) { map.put("msg", "账户已过期,登录失败!"); } else if (e instanceof CredentialsExpiredException) { map.put("msg", "密码已过期,登录失败!"); } else { map.put("msg", "登录失败!"); } PrintWriter out = resp.getWriter(); out.write(new ObjectMapper().writeValueAsString(map)); out.flush(); out.close(); }) .permitAll()// 最后还配置了permitAll,表示和登录相关的接口都不需要认证即可访问 .and() .logout()//表示开启注销登录的配置 .logoutUrl("/logout")//表示配置注销登录请求URL为“/logout”,默认也是“/logout” .clearAuthentication(true)//表示是否清除身份认证信息,默认为true,表示清除 .invalidateHttpSession(true)//表示是否使Session失效,默认为true .addLogoutHandler((req, resp, auth) -> { /* * 配置一个LogoutHandler,开发者可以在LogoutHandler中完成一些数据清除工作,例如Cookie的清除 */ }) .logoutSuccessHandler((req, resp, auth) -> resp.sendRedirect("/login_page"))// 配置一个LogoutSuccessHandler,开发者可以在这里处理注销成功后的业务逻辑,例如返回一段JSON提示或者跳转到登录页面等 .and().csrf().disable();//表示关闭csrf } }
多个HttpSecurity:
如果业务比较复杂,开发者也可以配置多个HttpSecurity,实现对WebSecurityConfigurerAdapter的多次扩展,代码如下
/** * 配置多个HttpSecurity时,MultiHttpSecurityConfig不需要继承WebSecurityConfigurerAdapter, * 在MultiHttpSecurityConfig中创建静态内部类继承WebSecurityConfigurerAdapter即可, */ @Configuration /* * 开发者也可以通过注解来灵活地配置方法安全,要使用相关注解,首先要通过@EnableGlobalMethodSecurity注解开启基于注解的安全配置 * prePostEnabled=true会解锁@PreAuthorize和@PostAuthorize两个注解,顾名思义, * @PreAuthorize注解会在方法执行前进行验证, * 而@PostAuthorize注解在方法执行后进行验证。 * securedEnabled=true会解锁@Secured注解。 */ @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) public class MultiHttpSecurityConfig { @Bean PasswordEncoder passwordEncoder() { /* * Spring Security提供了多种密码加密方案,官方推荐使用BCryptPasswordEncoder, * BCryptPasswordEncoder使用BCrypt强哈希函数,开发者在使用时可以选择提供strength和SecureRandom实例。 * strength越大,密钥的迭代次数越多,密钥迭代次数为2^strength。strength取值在4~31之间,默认为10。 */ return new BCryptPasswordEncoder(10); } @Autowired protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication() .withUser("root") .password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq") .roles("ADMIN", "DBA") .and() .withUser("admin") .password("$2a$10$RMuFXGQ5AtH4wOvkUqyvuecpqUSeoxZYqilXzbz50dceRsga.WYiq") .roles("ADMIN", "USER") .and() .withUser("sang") .password("$2a$10$eUHbAOMq4bpxTvOVz33LIehLe3fu6NwqC9tdOcxJXEhyZ4simqXTC") .roles("USER"); } @Configuration @Order(1) // 静态内部类上添加@Configuration注解和@Order注解,@Order注解表示该配置的优先级,数字越小优先级越大,未加@Order注解的配置优先级最小。 public static class AdminSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.antMatcher("/admin/**").authorizeRequests() .anyRequest().hasRole("ADMIN"); } } @Configuration public static class OtherSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() .and() .formLogin() .loginProcessingUrl("/login") .usernameParameter("name")//登录参数中用户名默认命名为username .passwordParameter("passwd")//密码默认命名为password .permitAll() .and() .csrf() .disable(); } } }
文章参考: Spring Boot+Vue全栈开发实战 - 10.1 Spring Security的基本配置