Spring Security 笔记(1)——入门

使用 spring-boot,依赖添加 starter-web 和 starter-security。

默认配置

对于一个没有任何额外配置的 controller,默认会使用 spring security 进行保护:

项目启动时会在控制台给出一个随机密码:

1
Using generated security password: 2fb8492d-410e-4333-998f-5fa5f68ed52f

使用用户名 user 和密码 2fb8492d-410e-4333-998f-5fa5f68ed52f 即可进行登录。

相关源码:

UserDetailsServiceAutoConfiguration 类:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
private String getOrDeducePassword(SecurityProperties.User user, PasswordEncoder encoder) {
    String password = user.getPassword();
    if (user.isPasswordGenerated()) {
        logger.info(String.format("%n%nUsing generated security password: %s%n", user.getPassword()));
    }
    if (encoder != null || PASSWORD_ALGORITHM_PATTERN.matcher(password).matches()) {
        return password;
    }
    return NOOP_PASSWORD_PREFIX + password;
}

SecurityProperties 类中的 User 静态内部类:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
/**
     * Password for the default user name.
     */
private String password = UUID.randomUUID().toString()

public void setPassword(String password) {
    if (!StringUtils.hasLength(password)) {
        return;
    }
    this.passwordGenerated = false;
    this.password = password;
}

如果 isPasswordGenerated() 返回 true,则用户没有配置密码,使用 UUID 生成密码。

设置密码

通过配置文件配置

在 spring 配置文件添加如下:

1
2
spring.security.user.name=bolitao
spring.security.user.password=bolitao

通过配置类配置

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("bolitao")
                .password("bolitao")
                .roles("USER", "ADMIN")
                .and()
                .withUser("testuser")
                .password("test")
                .roles("USER");
    }
}

以上代码以内存方式配置了两个用户:bolitaotestuser,并配置了他们的角色和密码。PasswordEncoder 暂时不管,后面会换成 bcrypt。

配置优先级:代码中的配置 > properties 中的配置

表单认证

做一个简陋的登录表单:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Login</title>
</head>
<body>
<img src="image/test.png" width="10%" alt="img">
<form action="/login" method="post">
    <div class="input">
        <label for="username">用户名</label>
        <input type="text" name="username" id="username">
        <span class="spin"></span>
    </div>
    <div class="input">
        <label for="password">密码</label>
        <input type="password" name="passwd" id="password">
        <span class="spin"></span>
    </div>
    <div class="button login">
        <button type="submit">
            <span>登录</span>
            <i class="fa fa-check"></i>
        </button>
    </div>
</form>
</body>
</html>

在 Spring Security 配置类添加:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Override
public void configure(WebSecurity web) throws Exception {
    web.ignoring().antMatchers("/image/**", "/css/**", "/js/**");
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
            .anyRequest().authenticated()
            .and()
            .formLogin()
            .loginPage("/login.html")
            .loginProcessingUrl("/login")
            .usernameParameter("username")
            .passwordParameter("passwd")
            .permitAll()
            .and()
            .logout()
            .logoutRequestMatcher(new AntPathRequestMatcher("/logout", "POST"))
            .and()
            .csrf().disable();
}

第一个 configure:对所有 URL 允许特定三个文件夹下的内容。

第二个 configure:

  1. 所有请求都需要认证
  2. 指定 form 方式登录,页面地址(GET)为 login.html,login API 地址(POST)为 /login;自定义表单 username 和 password 名为 usernamepasswd(一般不需要改,默认的语义更好);最后放行这两个地址
  3. 设定注销 URL 为 /logout(POST 方式)
  4. 暂关闭 CSRF

之后就可以使用自定义的页面,通过表单登录进行 Spring Security 认证了。

加载评论