项目创建

pom 依赖

1
2
3
4
5
6
7
8
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

Demo

写一个 Controller

1
2
3
4
5
6
7
8
@RestController
public class HelloController {

@RequestMapping(value = "/hello")
public String hello() {
return "hello";
}
}

启动项目测试,访问:http://localhost:8080/ ,默认跳转到登录页面

测试请求网址: http://localhost:8080/hello ,发现被 302 重定向到登录页面

只要加入依赖,项目的所有接口都会被自动保护起来,当用户从浏览器发送请求访问 /hello 接口时,服务端会返回 302 响应码,让客户端重定向到 /login 页面,用户在 /login 页面登录,登陆成功之后,就会自动跳转到 /hello 接口

默认情况下,登录的用户名是 user ,密码是项目启动时随机生成的字符串

登录以后可以发现 302 重定向到 /hello 接口

用户名密码配置

三种实现方案

配置文件

直接在 application.properties 文件中配置用户的基本信息

1
2
spring.security.user.name=admin
spring.security.user.password=123456

配置类

创建一个 Spring Security 的配置类,需要继承 WebSecurityConfigurerAdapter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 用 BCryptPasswordEncoder 实现加密和匹配密码
// 加密:encoder(),一个参数,放的是需要加密的密码(原始密码),返回值是String
// 匹配密码:matches(),两个参数,分别是原始密码和从数据库中查到的加密后的密码,返回值是boolean
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
// 对明文密码进行加密
String encode = passwordEncoder.encode("123456");
// 分别创建了两个角色:admin和user ,账户分别为root和zhang,密码都是123456
auth.inMemoryAuthentication().withUser("root").password(encode).roles("admin");
auth.inMemoryAuthentication().withUser("zhang").password(encode).roles("user");
}

@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

自定义实现类

一、自定义编写一个实现类,可以实现从数据库加载用户名和密码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 一个接口有多个实现类的情况下区分实现类的方式
@Service("userDetailsService")
// 自定义实现类实现了UserDetailsService接口
// 里面有一个loadUserByUsername(String s)方法,这个方法返回一个UserDetails
// UserDetails 是一个接口,而org.springframework.security.core.userdetails.User实现了UserDetails
// 因此这里我们可以直接使用SpringSecurity提供的User对象
// 当然如果不想使用SpringSecurity提供的User对象,我们也可以自己编写一个实现UserDetails接口的对象
public class UserDetailsServiceImpl implements UserDetailsService {

@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
List<GrantedAuthority> authorityList = AuthorityUtils.commaSeparatedStringToAuthorityList("role");
// 省略从数据库获取用户的过程
// 正常应该通过传入的用户名s去数据库查找对应的用户信息和权限
// 直接返回SpringSecurity提供的User对象
return new User("root", new BCryptPasswordEncoder().encode("123"), authorityList);
}
}

这里对 SpringSecurity 提供的 User 对象拓展更多的属性

继承 SpringSecurity 提供的 User 对象,拓展一个 userId 属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class LoginUser extends User {
private Integer userId;

public LoginUser(String username, String password, Collection<? extends GrantedAuthority> authorities) {
super(username, password, authorities);
}

public LoginUser(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
}

public Integer getUserId() {
return userId;
}

public void setUserId(Integer userId) {
this.userId = userId;
}
}

修改好的实现类

1
2
3
4
5
6
7
8
9
10
11
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
List<GrantedAuthority> authorityList = AuthorityUtils.commaSeparatedStringToAuthorityList("role");
// 省略从数据库获取用户的过程
// 正常应该通过传入的用户名s去数据库查找对应的用户信息和权限
// 用户名随便输入,传入的s就是用户名
LoginUser loginUser = new LoginUser(s, new BCryptPasswordEncoder().encode("123456"), authorityList);
// 直接加上我们增加的用户属性
loginUser.setUserId(00001);
return loginUser;
}

二、创建配置类,指定使用哪个 userDetailService 实现类

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
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Resource
private UserDetailsService userDetailsService;

/**
* 实现身份认证
*
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 注入自定义的实现类,这样SpringSecurity会调用里面的loadUserByUsername(String s)方法
auth.userDetailsService(userDetailsService);
}

/**
* 密码加密方式:强散列哈希加密实现
*
* @return
*/
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

测试访问:http://localhost:8080/hello