0%

Spring Security——注解的使用、RememberMe功能实现

注解的使用

  • 在使用SpringSecurity的注解之前,需要开启注解功能,可以选择在启动类上或配置类上添加@EnableGlobalMethodSecurity(securedEnabled = true),表示开启了@Secured注解的功能
1
2
3
4
5
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
...
}

@Secured

  • 该注解在Controller类上或方法上添加,表示进入该控制器或者方法需要什么角色,需要注意的是角色需要加“ROLE_”前缀。注意,它只能用于角色认证。
  • 注释掉配置类中的hasRolehasAnyRole方法,然后在控制器上添加@Secured注解。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@RestController
@RequestMapping("/test")
//表示index和hello的url需要ROLE_admin和ROLE_guest两个角色之中的一个
@Secured({"ROLE_admin", "ROLE_guest"})
public class TestController {
@GetMapping("index")
public String index(){
return "hello,index";
}
@GetMapping("hello")
public String hello(){
return "hello,security";
}
}
/*******************MyUserDetailsService的修改*******************/
List<GrantedAuthority> roles = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_guest");

@PreAuthorize

  • 使用该注解之前需要在配置类中的@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true),prePostEnabled设置为true表示开启@PreAuthorize@PostAuthorize@PreFilter@PostFilter功能。

  • 该注解在Controller类上或方法上添加,在里面可以加入hasRolehasAuthority等认证权限的方法,表示进入该控制器或者方法之前需要什么权限。该注解可以认证角色和权限。

  • 注释掉配置类中的hasAuthorityhasAnyAuthority方法,把@Secured注解注释掉,然后在控制器的方法上添加@PreAuthorize注解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@RestController
@RequestMapping("/test")
//@Secured({"ROLE_guest","ROLE_admin"})
public class TestController {
@GetMapping("index")
//表示需要index权限
@PreAuthorize("hasAnyAuthority('index')")
public String index(){
return "hello,index";
}
@GetMapping("hello")
//表示需要hello角色
@PreAuthorize("hasAnyRole('ROLE_hello')")
public String hello(){
return "hello,security";
}
}
/*******************MyUserDetailsService的修改*******************/
List<GrantedAuthority> roles = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_hello,index");

@PostAuthorize

  • 同样也需要在配置类中开启注解功能,prePostEnabled设置为true。
  • 该注解在Controller类上或方法上添加,在里面可以加入hasRolehasAuthority等认证权限的方法,表示该控制器或者方法执行之后需要什么权限。该注解可以认证角色和权限。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@RestController
@RequestMapping("/test")
//@Secured({"ROLE_guest","ROLE_admin"})
public class TestController {
@GetMapping("index")
//表示需要index权限
@PostAuthorize("hasAnyAuthority('index')")
public String index(){
return "hello,index";
}
@GetMapping("hello")
//表示需要hello角色
@PostAuthorize("hasAnyRole('ROLE_hello')")
public String hello(){
return "hello,security";
}
}
/*******************MyUserDetailsService的修改*******************/
List<GrantedAuthority> roles = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_hello,index");

@PreFilter

  • 同样也需要在配置类中开启注解功能,prePostEnabled设置为true。

  • 该注解表示在进入控制器的方法之前对数据进行过滤。该注解用得比较少,只作简单说明

1
2
3
4
5
6
7
8
9
10
11
12
@RequestMapping("getTestPreFilter")
@PreAuthorize("hasRole('ROLE_admin')")
//这里表示过滤出id%2==0的用户信息,作为输入参数
@PreFilter(value = "filterObject.id%2==0")
@ResponseBody
public List<UserInfo> getTestPreFilter(@RequestBody List<UserInfo>
list){
list.forEach(t-> {
System.out.println(t.getId()+"\t"+t.getUsername());
});
return list;
}

@PostFilter

  • 同样也需要在配置类中开启注解功能,prePostEnabled设置为true。

  • 该注解表示在控制器的方法执行之后对数据进行过滤。该注解用得比较少,只作简单说明

1
2
3
4
5
6
7
8
9
10
11
@RequestMapping("getAll")
@PreAuthorize("hasRole('ROLE_admin')")
//这里表示过滤出username等于admin1的用户信息,作为返回结果
@PostFilter("filterObject.username == 'admin1'")
@ResponseBody
public List<UserInfo> getAllUser(){
ArrayList<UserInfo> list = new ArrayList<>();
list.add(new UserInfo(1l,"admin1","6666"));
list.add(new UserInfo(2l,"admin2","888"));
return list;
}

用户注销

  • 只需要在配置类中添加登出的url和登出成功后跳转的地址就可以了。
1
2
3
4
5
6
7
8
@Override
protected void configure(HttpSecurity http) throws Exception {
http.logout()
.logoutUrl("/logout") //指定登出的url
.logoutSuccessUrl("/login.html") //指定登出后跳转的页面
.invalidateHttpSession(true) //登出是否清空session
.permitAll()
}

RememberMe功能

  • 以前记住我的功能的实现,是把登录的状态会保存到一个cookie中,存放在浏览器中,当下一次需要登录时,或者打开一个新的浏览器进行登录,会提前判断浏览器中是否有该用户的cookie,如果有就免登录。使用cookie技术有一个缺点就是如果cookie中有敏感信息,就可能导致泄密。

实现原理

  • SpringSecurity实现记住我功能和cookie技术不同,它是不会泄露敏感信息的,它的实现过程如下:
    1. 首先,如果通过浏览器认证成功,security会将一个存有加密串的cookie存放到浏览器中
    2. 与此同时,也将加密串和对应的用户信息字符串存储到数据库,这个可以在PersistentTokenRepository类中看到
    3. 再次访问时,security会获取到浏览器的cookie信息到数据库中进行比对,如果查询到对应的信息则认证成功,可以直接登录,否则,认证失败。

Snipaste_2021-02-01_11-46-55

Snipaste_2021-02-01_11-51-57

实现

  • 首先,实现基于数据库的记住我功能,需要添加springboot关于jdbc的依赖
1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
  • 实现基于数据库的记住我功能,需要创建一张表。其实,如果没有创建该表,SpringSecurity也会帮我们创建。
1
2
3
4
5
6
7
8
CREATE TABLE `persistent_logins` (
`username` varchar(64) NOT NULL, /* 用户名 */
`series` varchar(64) NOT NULL, /* 加密的信息串 */
`token` varchar(64) NOT NULL, /* cookie中的token信息 */
`last_used` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE
CURRENT_TIMESTAMP, /* 最后使用的日期 */
PRIMARY KEY (`series`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  • 添加数据库的配置文件
1
2
3
4
5
6
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://xxx/securityDemo
username: root
password: xxx
  • 然后在配置类中注入数据源对象,并创建一个实现PersistentTokenRepository接口的实现类的对象。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private DataSource dataSource;
@Bean
public PersistentTokenRepository persistentTokenRepository(){
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
jdbcTokenRepository.setCreateTableOnStartup(true);
return jdbcTokenRepository;
}
protected void configure(HttpSecurity http) throws Exception {
//配置rememberme功能
http.rememberMe()
.tokenRepository(persistentTokenRepository() //设置tokenRepository
.tokenValiditySeconds(60) //设置cookie的过期时间
.userDetailsService(userDetailsService); //设置用户的来源
...
}
...
}
  • 在login.html中添加复选框
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/user/login", method="post">
用户名:<input type="text", name="username">
<br>
密码:<input type="text", name="password">
<br>
<!--这里的name必须是remember-me-->
<input type="checkbox" name="remember-me">自动登录
<br>
<input type="submit", value="登录">
</form>
</body>
</html>
  • 运行项目,查看效果。
-------------本文结束感谢您的阅读-------------