SpringBoot Spring Security的基本配置

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的基本配置

posted @ 2022-06-20 16:54  草木物语  阅读(799)  评论(0编辑  收藏  举报