Spring Security 下
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.运行测试
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.运行测试
上述两个操作已经足够了
登录页面之后,会出现在 成功登录页面 ,这时候 你进入其他的页面也是可以的(如果权限允许)
点击退出,返回设置好的页面,这时候 其他页面都无法进入
基于数据库实现记住我
步骤:
-
创建数据库
-
配置类 、配置数据源
-
配置类中 配置 自动登录
-
登陆页面添加 复选框
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 中 ; 也因为数据库为框架内部设置好的,所以也会存在数据库表中
CSRF 保护
CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack“或者Session Riding,通常缩写为CSRF或XSRF,是一种对网站的恶意攻击。是一种依赖web浏览器的、被混淆过的代理人攻击。
常见特性:
- 依靠用户标识危害网站
- 利用网站对用户表识的信任
- 欺骗用户的浏览器发送HTTP请求给目标站点
- 可以通过IMG标签会触发一个GET请求,可以利用它来实现CSRF攻击。
该种方式只能对
POST
、PUT
、DELETE
、PATCH
发送方式进行保护
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开启了,这时候进入测试路径:
输入账号密码,会成功进入 /csrf_success ; 并且页面中显示 _csrf.token