认证和授权学习2:springboot中快速使用spring security

本文以一个示例工程记录下如何在springboot工程中快速的使用spring security,使用的springboot版本是
2.1.3.RELEASE

一、创建工程,导入依赖

创建一个springboot工程,导入springsecurity的起步依赖.

       <!-- 以下是>spring boot依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- 以下是>spring security依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

二、默认配置

这个时候启动应用,这个应用就已经被保护了,springsecurity提供了一个默认用户user,对应的密码在启动工程时会打印在控制台上,如

Using generated security password: c07618df-93d6-4d0c-88df-9c373d2131e3

这个时候访问应用中的一个资源,就会跳转到springsecurity默认的一个登录页面,输入账号密码后会跳转到登录前要访问的url

三、自定义配置

主要涉及2个方面,用户配置,安全配置,通过创建一个配置类继承WebSecurityConfigurerAdapter来配置

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    //1.用户配置
    //2.安全配置
}

3.1用户配置

配置时springsecurity提供了两种用户配置,简单点的可以直接在内存中配置用户对象,用于测试;也可以连接数据库进行持久化配置

3.1.1 在内存中配置用户

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    //1.用户配置
    @Bean
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("lyy").password("123").authorities("ROLE_p1").build());
        manager.createUser(User.withUsername("zs").password("123").authorities("ROLE_ADMIN").build());
        return manager;
    }
    //1.1用户密码的加密方式
    @Bean
    public PasswordEncoder passwordEncoder(){
        return NoOpPasswordEncoder.getInstance();
        //return new BCryptPasswordEncoder();
    }
    //2.安全配置
}

springsecurity提供了一个InMemoryUserDetailsManager对象来在内存中创建用户。在上边的代码中,创建了两个用户并赋予不同的权限。

需要注意的是,要配置验证用户密码时使用的加密方式,如果不想加密,可以配置NoOpPasswordEncoder的对象,表示不加密。

3.1.2 使用数据库中的用户对象

springsecurity提供了一个UserDetailsService接口来查询用户,要从数据库查询用户,只需要创建一个这个接口的bean并加入spring容器中,然后移除内存用户的相关配置,但编码器还要保留。

@Component
public class MyUserDetailService implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //模拟从数据库查询用户信息,这里这个UserDetails中的用户信息都是从数据库查询到的
        System.out.println("当前请求登录的用户:"+username);
        UserDetails userDetails = User.withUsername(username).password("111")
                .authorities("p1").build();
        return userDetails;
    }
}

实际上就是实现了loadUserByUsername方法,返回了一个UserDetails对象给springsecurity

3.2安全配置

安全配置通过重写configure方法来实现。主要设置这几个方面

(1)哪些url被保护,需要什么权限才能访问。这里要注意的是,用hasRole指定角色时不带ROLE_前缀,但是配置用户时要带上ROLE_前缀。匹配url时,越具体的url要先匹配。

(2)配置登录页面和处理登录请求的url,注意自定义安全配置时一定要把登录页面和登录url配上,不然访问任何页面都是403,除非最后一行调用父类的config方法

(3)退出和失效的相关配置.

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    //1.用户配置
    ...
    //2.安全配置
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        /**
         * 设置对url的保护时,更具体的规则应该先写,模糊匹配的要写在最后,
         * 因为一次请求匹配到第一个规则后就会忽略后边的规则
         */

        http.csrf().disable();
        //这两个资源要有特定的权限才可以访问
        http.authorizeRequests().antMatchers("/r/r1").hasRole("p1");
        http.authorizeRequests().antMatchers("/r/r2").hasRole("p2");
        //配置这个目录下的资源要登录才可以访问,不配置的话不拦截,不登录页可以访问
        http.authorizeRequests().antMatchers("/r/**").authenticated();
        http.authorizeRequests().anyRequest().permitAll();
        //因为下边这个配置针对的url在上边那个配置已经匹配过了,所以下边这个不生效
        http.authorizeRequests().antMatchers("/test1.html").authenticated();
        http.formLogin()
                .loginPage("/login.html")
                //处理登录请求的url
                .loginProcessingUrl("/login");
                //登录成功后的跳转url,如果不配置,默认跳转到登录前要访问的页面
                //.successForwardUrl("/login-success");
        //这个配置配置的是多个人登录同一账号时,后边的把前边的挤掉后,把前边的重定向到的页面
        http.sessionManagement().maximumSessions(1).expiredUrl("/sessionExpired.html");
        //这个配置退出的url和退出后跳转到的页面
        http.logout().logoutUrl("/mylogout").logoutSuccessUrl("/logoutSuccess.html");
    }
}

注意这里所有的html页面都放在springboot的静态资源目录中,这样才能被访问到,我是放在了resources/static目录。

登录页面的内容如下

<html>
<head>
    <meta charset="UTF-8">
    <title>login</title>
</head>
<body>
login
<form action="login" method="post">
    用户名:<input type="text" name="username"><br>
    密&nbsp;&nbsp;&nbsp;码:
    <input type="password" name="password"><br>
    <input type="submit" value="登录">
</form>
</body>
</html>

注意这里登录表单的action写的是相对路径,如果要写绝对路径,就要带上当前应用的路径,即yml中的配置

server:
  port: 8080
  servlet:
    context-path: /springsecurity-springboot01

可以在这里查看完整的示例工程

示例工程