SpringSecurity 入门(一)
/**
返回密码编码器
*/
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
/**
配置用户账号密码以及角色
@param auth
@throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//在内存中创建用户
auth.inMemoryAuthentication()
//账号
.withUser("admin")
//密码,需要加密
.password(new BCryptPasswordEncoder().encode("123"))
//添加角色
.roles("ADMIN","USER")
//创建另一个用户
.and()
.withUser("user")
.password(new BCryptPasswordEncoder().encode("123"))
.roles("USER");
}
/**
配置 web 页面的权限
@param http
@throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
//用户请求授权
http.authorizeRequests()
//指定登录相关的请求,permitAll 是不需要验证
.antMatchers("/login").permitAll()
//指定/user/** 需要 USER 角色
.antMatchers("/user/**").hasRole("USER")
.antMatchers("/admin/**").hasRole("ADMIN")
//其它所有 URL 都需要验证
.anyRequest().authenticated()
.and()
//配置登录 URL 为 login,登录成功后跳转 main
.formLogin().loginPage("/login").defaultSuccessUrl("/main")
.and()
//配置注销 url,注销后到登录页面
.logout().logoutUrl("/logout").logoutSuccessUrl("/login");
}
}
测试 login,输入上面配置的账号和密码
登录成功
账号密码填写错误
==================================================================
SpringSecurity 登录验证使用的密码必须要经过加密处理,这里提供了 PasswordEncoder 接口进行密码加密。
PasswordEncoder 接口提供两个主要方法:
String encode(CharSequence rawPassword)
将原始密码加密,返回密文
boolean matches(CharSequence rawPassword,String password)
将第一个参数原始密码和第二个参数密文进行匹配,返回是否匹配成功
PasswordEncoder 的常用实现类是:BCryptPasswordEncoder
BCryptPasswordEncoder 是基于 hash 算法的单向加密,可以控制密码强度,默认为 10。
在上面的配置类中,配置了该加密器
@Bean
public PasswordEncoder passwordEncoder(){
return new
BCryptPasswordEncoder();
}
做下测试
@SpringBootTest
class SpringSecurityDbDemoApplicationTests {
@Autowired
private PasswordEncoder passwordEncoder;
@Test
void contextLoads() {
for (int i = 0; i < 5; i++) {
String encode = passwordEncoder.encode("123456");
System.out.println("encode:"+encode);
System.out.println("matches:"+passwordEncoder.matches("123456",encode));
}}
}
可以看到同样是对"123456"进行加密,每次得到的密文都不相同,但是每次都可以匹配成功。
不同于另一个常用的安全框架:Shiro,SpringSecurity 不需要给密码单独配置盐,盐是随机生成的,这样密码的安全性更高。
==================================================================
在创建用户时,除了账号密码外,还可以添加对应的角色和权限,如:
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//在内存中创建用户
auth.inMemoryAuthentication()
//账号
.withUser("admin")
//密码,需要加密
.password(new BCryptPasswordEncoder().encode("123"))
//添加角色
.roles("ADMIN","USER")
//创建另一个用户
.and()
.withUser("user")
.password(new BCryptPasswordEncoder().encode("123"))
//也可以通过 authorities 添加权限和角色,如果是角色需要以 ROLE_开头
.authorities("LIST","ROLE_USER");
}
给指定的 URL 配置角色和权限,这样就可以进行访问控制了
@Override
protected void configure(HttpSecurity http) throws Exception {
//用户请求授权
http.authorizeRequests()
//指定 toLogin 请求,permitAll 不需要验证
.antMatchers("/login").permitAll()
//指定/user/** 需要 USER 角色
.antMatchers("/user/**").hasRole("USER")
.antMatchers("/admin/**").hasRole("ADMIN")
//需要 LIST 权限
.antMatchers("/admin/**").hasAuthority("LIST")
//其它所有 URL 都需要验证
.anyRequest().authenticated()
.and()
//配置登录页面为 login,登录成功后跳转 main
.formLogin().loginPage("/login").defaultSuccessUrl("/main")
.and()
//配置注销 url,注销后到登录页面
.logout().logoutUrl("/logout").logoutSuccessUrl("/login");
}
修改控制器的/main 方法
@RequestMapping("/main")
public String main(Model model){
//读取验证对象
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
//读取用户
Object principal = authentication.getPrincipal();
//如果是登录用户,则为 org.springframework.security.core.userdetails.User
if(principal instanceof User){
User user = (User) principal;
//读取用户名
model.addAttribute("username",user.getUsername());
//读取所有权限
model.addAttribute("authorities",user.getAuthorities());
}else {
model.addAttribute("username", principal);
}
return "main";
}
修改 main.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Main</title>
</head>
<body>
<h1 th:text="|Welcome!${username}|">Welcome!</h1>
你的权限有:
<span th:each="auth:${authorities}">
[[${auth}]]
</span>
<p><a href="/admin/admin">管理员页面</a> </p>
<p><a href="/user/user">用户页面</a> </p>
<p><a href="/login">登录页面</a> </p>
<p>
<form th:action="@{/logout}" method="post">
<input type="submit" value="注销">
</form>
</p>
</body>
</html>
用 admin 登录,看到的权限是两个角色:ROLE_ADMIN 和 ROLE_USER,ROLE_是自动添加到角色上的。
用 user 登录
再试一下访问不同的 URL
添加目录和文件:admin/admin.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Admin</title>
</head>
<body>
<h1 th:text="|Hello!${username},欢迎进入管理员页面|">Admin</h1>
</body>
</html>
user/user.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>User</title>
</head>
<body>
<h1 th:text="|Hello!${username},欢迎进入用户页面|">User</h1>
</body>
</html>
错误页面:error/403.html,这里/error 是 Security 默认的错误地址,添加/error/错误代码.html 后,出现对应错误时会自动跳转到对应页面,403 是权限不足。
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>403</title>
</head>
<body>
Sorry!!你的权限不足
</body>
</html>
控制器添加方法:
@RequestMapping("/user/user")
public String user(Model model){
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Object principal = authentication.getPrincipal();
if(principal instanceof User){
User user = (User) principal;
model.addAttribute("username",user.getUsername());
model.addAttribute("authorities",user.getAuthorities());
}else {
model.addAttribute("username", principal);
}
return "user/user";
}
@RequestMapping("/admin/admin")
public String admin( Model model){
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Object principal = authentication.getPrincipal();
if(principal instanceof User){
User user = (User) principal;
model.addAttribute("username",user.getUsername());
model.addAttribute("authorities",user.getAuthorities());
}else {
model.addAttribute("username", principal);
}
return "admin/admin";
}
用 admin 登录后,访问管理员的超链接,出现了权限不足
访问用户超链接正常
admin 登录后只有 ADMIN 和 USER 角色,没有 LIST 权限,所以不能访问/admin/admin,可以修改创建 admin 用户时的授权配置,就可以访问了
//添加角色
// .roles("ADMIN","USER")
//添加 ADMIN、USER 角色和 LIST 权限,如果是角色需要以 ROLE_开头
评论