Spring Security 下

Security 注解使用

1.@Secured

判断是否具有角色,另外需要注意的是这里匹配的字符串需要添加前缀 ROLE

1.在 启动类 或者 配置类 上加入注解 开启该注解

@EnableGlobalMethodSecurity(securedEnabled = true)

2.Controller层加入方法注解

@GetMapping("/update")
@Secured({"ROLE_sale","ROLE_man"}) //有该权限才可以
public String update(){
return "update";
}

3.在UserDetailsService实现类中添加权限

List<GrantedAuthority> auths =
AuthorityUtils.commaSeparatedStringToAuthorityList("admins,ROLE_user");

4.运行测试

你登陆之后,如果登录用户没有对应权限,就算是可以登陆上去,也无法进入该路径

2.@PreAuthorize

该注解适合进入方法前的权限验证 ,可以将登录用户的 roles/permissions 参数传到方法中。.

1.在 启动类 或者 配置类 上加入注解 开启该注解

@EnableGlobalMethodSecurity(prePostEnabled = true)

2.Controller层加入方法注解

@GetMapping("/update")
@PreAuthorize("hasAnyAuthority('admins')")
/* @PreAuthorize("hasAnyAuthority('admins')")
* @PreAuthorize("hasRole('admins')")
*/ @PreAuthorize("hasAnyRole('admins')")
public String update(){
return "update";
}

3.在UserDetailsService实现类中添加权限

List<GrantedAuthority> auths =
AuthorityUtils.commaSeparatedStringToAuthorityList("admins,ROLE_user");

4.运行测试

该方法,与在配置类中给网页加上进入权限相同,连四个方法都完全一样

3.@PostAuthorize

该注解使用不多,在方法执行后再进行权限验证,适合验证带有返回值的权限

1.在 启动类 或者 配置类 上加入注解 开启该注解

@EnableGlobalMethodSecurity(prePostEnabled = true)

2.Controller层加入方法注解

@GetMapping("/update")
@PostAuthorize("hasAnyAuthority('admin')") //也是参数为 四种方法
public String update(){
System.out.println("update。。。");
return "update";
}

3.在UserDetailsService实现类中添加权限

List<GrantedAuthority> auths =
AuthorityUtils.commaSeparatedStringToAuthorityList("admins,ROLE_user");

4.运行测试

该注解下,虽然权限不足仍然无法进入页面或者返回信息,但是可以先运行方法体中的内容,再校验

所以对于上面的实例,进入路径后,虽然还是显示 没权限403,但是 update 会输出

4.@PostFilter

权限验证之后对返回的数据进行过滤

1.在 启动类 或者 配置类 上加入注解 开启该注解

@EnableGlobalMethodSecurity(prePostEnabled = true)

2.Controller层加入方法注解

@GetMapping("getAll")
@PostAuthorize("hasAnyAuthority('admins') ")
@PostFilter("filterObject.username == 'admin1'")
public List<Users> getAllUser() {
ArrayList<Users> list = new ArrayList<>();
list.add(new Users(1, "admin1", "666"));
list.add(new Users(2, " admin2", "888"));
return list;
}

3.在UserDetailsService实现类中添加权限

List<GrantedAuthority> auths =
AuthorityUtils.commaSeparatedStringToAuthorityList("admins,ROLE_user");

4.运行测试

1

5.@PreFilter

进入控制器之前对传递回来的数据进行过滤

@PostFilter 类似, 不做演示

用户注销

1.创建登录成功页面

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
登录成功!
<a href="/logout">退出</a>
</body>
</html>

2.配置类中配置注销功能

@Override
protected void configure(HttpSecurity http) throws Exception {
//注销,第一个路径为 退出是设置的路径 ; 第二个路径为 成功退出后的路径
http.logout().logoutUrl("/logout").logoutSuccessUrl("/login.html").permitAll();
http.exceptionHandling().accessDeniedPage("/uuauth.html");
http.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/user/login")
.defaultSuccessUrl("/success.html").permitAll() //这里更换为登录成功页面
.and().authorizeRequests()
.antMatchers("/","/hello","/user/login")
.permitAll()
.antMatchers("/index").hasRole("user")
.anyRequest().authenticated()
.and().csrf().disable();
}

3.运行测试

上述两个操作已经足够了

登录页面之后,会出现在 成功登录页面 ,这时候 你进入其他的页面也是可以的(如果权限允许)

点击退出,返回设置好的页面,这时候 其他页面都无法进入

基于数据库实现记住我

2

步骤:

  1. 创建数据库

  2. 配置类 、配置数据源

  3. 配置类中 配置 自动登录

  4. 登陆页面添加 复选框


1.创建数据库

该数据库语句是 Security 框架中 内置的创建语句

CREATE TABLE `persistent_logins`( 
`username` varchar(64) NOT NULL,
`series` varchar(64) NOT NULL,
`token` varchar(64) NOT NULL,
`last_used` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (series)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

2.配置数据源

@Autowired
private DataSource dataSource;
@Bean
public PersistentTokenRepository persistentTokenRepository(){
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
// jdbcTokenRepository.setCreateTableOnStartup(true); 启动时 创建表
return jdbcTokenRepository;
}

3.配置自动登录

@Override
protected void configure(HttpSecurity http) throws Exception {
http.logout().logoutUrl("/logout").logoutSuccessUrl("/login.html").permitAll();
http.exceptionHandling().accessDeniedPage("/uuauth.html");
http.formLogin()
.loginPage("/login.html")
.loginProcessingUrl("/user/login")
.defaultSuccessUrl("/success.html").permitAll()
.and().authorizeRequests()
.antMatchers("/","/hello","/user/login")
.permitAll()
.antMatchers("/index").hasRole("user")
.anyRequest().authenticated()

.and().rememberMe().tokenRepository(persistentTokenRepository())
.tokenValiditySeconds(60) //设置有效时长
.userDetailsService(userDetailsService)

.and().csrf().disable();
}

4.登陆页面添加 记住我

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="post" action="/user/login">
用户名:<input type="text" name="username">
<br>
密码:<input type="text" name="password">
<br>
<input type="checkbox" name="remember-me"> 自动登录 <!-- name 必须为 "remember-me" -->
<br>
<input type="submit" value="login">
</form>
</body>
</html>

5.运行测试

等着之后,数据会存在 cookie 中 ; 也因为数据库为框架内部设置好的,所以也会存在数据库表中

3

4

CSRF 保护

CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack“或者Session Riding,通常缩写为CSRF或XSRF,是一种对网站的恶意攻击。是一种依赖web浏览器的、被混淆过的代理人攻击。

常见特性:

  • 依靠用户标识危害网站
  • 利用网站对用户表识的信任
  • 欺骗用户的浏览器发送HTTP请求给目标站点
  • 可以通过IMG标签会触发一个GET请求,可以利用它来实现CSRF攻击。

该种方式只能对 POSTPUTDELETEPATCH 发送方式进行保护

1.引入依赖

<!-- thymeleaf 引擎 -->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
<version>3.0.2.RELEASE</version>
</dependency>

2.创建CSRF测试前端页面

先直接在static文件夹下创建一个login页面

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="post" action="/user/login">
用户名:<input type="text" name="username">
<br>
密码:<input type="text" name="password">
<br>
<input type="checkbox" name="remember-me"> 自动登录
<br>
<input type="submit" value="login">
</form>
</body>
</html>

首先在resources文件夹下创建 templates 文件夹, Spring Boot 默认Controller层跳转路径为该文件夹下页面

csrf.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form method="post" action="token">
<input type="hidden" th:name= "${_csrf.parameterName}" th:value="${_csrf.token}" />
用户名:<input type="text" name="username">
<br>
密码:<input type="text" name="password">

<input type="submit" value="login">
</form>
</body>
</html>

csrf_success.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<span th:text="${_csrf.token}"></span> <br>
</body>
</html>

3.创建Controller层代码

@Controller
public class CSRFController {

@GetMapping("/csrf")
public String csrf(Model model) {
return "csrf";
}
@PostMapping("/csrf_success")
public String success() {
return "csrf_success";
}
}

4.修改配置类代码

http.formLogin()
.loginProcessingUrl("/user/login")
.defaultSuccessUrl("/").permitAll()
.and().authorizeRequests()
.antMatchers("/","/csrf","/user/login")
.permitAll();
// .and().csrf().disable(); //关闭csrf

5.运行测试

这时候已经将CSRF开启了,这时候进入测试路径:

http://localhost:8081/csrf

输入账号密码,会成功进入 /csrf_success ; 并且页面中显示 _csrf.token

5