SpringSecurity授权
通过上一章SpringBoot+SpringSecurity整合(一)简单的使用了SpringSecurity,这一章将详细讲解SpringSecurity如何去授权
数据库编写
sys_user表
DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`password` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
`del` int(1) UNSIGNED ZEROFILL NOT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
sys_role表
CREATE TABLE `sys_role` (
`id` int(11) NOT NULL COMMENT '主键',
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '角色名称',
`del` int(1) UNSIGNED ZEROFILL NOT NULL DEFAULT 0 COMMENT '逻辑删除标识1已删除,0未删除',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
sys_auth表
DROP TABLE IF EXISTS `sys_auth`;
CREATE TABLE `sys_auth` (
`id` int(11) NOT NULL,
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
`del` int(1) UNSIGNED ZEROFILL NOT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;
sys_role_auth表
DROP TABLE IF EXISTS `sys_role_auth`;
CREATE TABLE `sys_role_auth` (
`auth_id` int(11) NOT NULL,
`role_id` int(11) NOT NULL,
PRIMARY KEY (`auth_id`, `role_id`) USING BTREE,
INDEX `角色`(`role_id`) USING BTREE,
CONSTRAINT `权限` FOREIGN KEY (`auth_id`) REFERENCES `sys_auth` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT,
CONSTRAINT `角色` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_bin ROW_FORMAT = Dynamic;
sys_user_role表
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role` (
`user_id` int(11) NOT NULL,
`role_id` int(11) NOT NULL,
PRIMARY KEY (`user_id`, `role_id`) USING BTREE,
INDEX `fk_role_id`(`role_id`) USING BTREE,
CONSTRAINT `fk_role_id` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
CONSTRAINT `fk_user_id` FOREIGN KEY (`user_id`) REFERENCES `sys_user` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
实体类,Service省略不贴上,yml文件中要加入mysql的配置信息,数据库数据请自行初始化,后续加入Mybatis-plus和mysql依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
编写MySecurityUserDetailsService
UserDetailsService
是SpringSecurity
进行权限和角色校验的接口,所以首先得编写这一个类,自定义一个类实现这个接口,并且重写其中的loadUserByUsername方法,把账号密码、用户名、角色和权限放到UserDetails
中,这样SpringSecurity
才能校验到当前用户是否具有访问当前页面或者访问某一个数据的权限,这里才放入角色的时候要加上ROLE_
前缀这个是SpringSecurity规定的
@Configuration
public class MySecurityUserDetailsService implements UserDetailsService {
@Autowired
private SysUserService sysUserService;
@Autowired
private SysRoleService sysRoleService;
@Autowired
private SysAuthService sysAuthService;
/**
*
* @param name 当前登录用户的用户名
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
// 声明一个GrantedAuthority集合存放权限和角色信息
List<GrantedAuthority> authorities = new ArrayList<>();
QueryWrapper<SysUser> queryWrapper=new QueryWrapper<>();
queryWrapper.eq("name",name);
// 获取到当前用户
SysUser loginUser = sysUserService.getOne(queryWrapper);
if(loginUser==null){
throw new UsernameNotFoundException("没有当前用户");
}
Long id = loginUser.getId();
// 获取到相关用户的角色
List<SysRole> sysRoleList=sysRoleService.getList(id);
// 获取到相关用户的权限
List<SysAuth> sysAuthList=sysAuthService.getList(id);
sysRoleList.stream().forEach(item->{
// 创建一个GrantedAuthority实现对象存放roleName
SimpleGrantedAuthority simpleGrantedAuthority=new SimpleGrantedAuthority(item.getName());
// 储存当前用户角色
authorities.add(simpleGrantedAuthority);
});
sysAuthList.stream().forEach(item->{
// 创建一个GrantedAuthority实现对象存放roleName
SimpleGrantedAuthority simpleGrantedAuthority=new SimpleGrantedAuthority(item.getName());
// 储存当前用户权限信息
authorities.add(simpleGrantedAuthority);
});
// 返回当前User对象
return new User(loginUser.getName(),loginUser.getPassword(),authorities);
}
}
编写MyWebSecurityConfig
WebSecurityConfigurerAdapter
是SpringSecurity编写如何校验权限,对哪一些资源进行权限校验,编写一个MyWebSecurityConfig
类继承WebSecurityConfigurerAdapter
并且重写其中的configure(HttpSecurity http)
方法和configure(AuthenticationManagerBuilder auth)
方法,这里再讲一下BCryptPasswordEncoder
类,这一个类是SpringSecurity提供出来对密码加密的加密方式的类
// 测试BCryptPasswordEncoder类,每一次加密之后的密文都是不一样的,只能通过该类的matches方法去判断原文密码和密文是否匹配,想增加加密的程度可以更改加密的长度
@Test
public void contextLoads() {
PasswordEncoder pw=new BCryptPasswordEncoder();
String encode = pw.encode("123"); // 对密码进行加密
System.out.println("encode = " + encode); // 输出加密之后的密码
boolean matches = pw.matches("123", "$2a$10$VAlFeRQeRIftrObRvpfB0utw6FezoSPSRh/1vT5snq3U0zyWOGBB6");// 判断面是否正确
System.out.println("matches = " + matches);
}
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean("bCryptPasswordEncoder")
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Autowired
private MyAccessDeniedHandler myAccessDeniedHandler;
@Autowired
private MySecurityUserDetails mySecurityUserDetails;
@Override
protected void configure(HttpSecurity http) throws Exception {
// 表单登录
http.formLogin()
// 设置登录页面
.loginPage("/login.html")
// 登录的请求必须和表单登录的url一致
.loginProcessingUrl("/login")
// 登录成功的页面(这里不能直接写页面要通过POST请求跳到页面)
.defaultSuccessUrl("/toMain")
// 登录失败的页面(这里不能直接写页面要通过POST请求跳到页面)
.failureForwardUrl("/toError");
// 授权
http.authorizeRequests()
// 对登录页面、登录失败的请求不做授权访问
.antMatchers("/login.html","/error.html").permitAll()
// 其他所有的请求必须得经过授权,必须登录
.anyRequest().authenticated();
// 关闭CSRF跨域(这里必须先关掉否则每次请求必须带上_csrf参数才能被识别)
http.csrf().disable();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 使用当前自己定义的mySecurityUserDetails进行密码校验,密码加密方式BCryptPasswordEncoder
auth.userDetailsService(mySecurityUserDetails).passwordEncoder(passwordEncoder());
}
}
SpringSecurity记住密码的功能
首先记住密码是很多登录的时候都用到的功能,
SpringSecurity
内置加入了记住密码的功能首先得在html登录处加入,参数必须是remember-me
命名
<div>
记住密码:<input type="checkbox" name="remember-me" >
</div>
在SpringSecurity配置类中注册一个Bean:
jdbcTokenRepository
如下:
/**
* 记住密码主要是通过token实现的,用户需要记住密码,通过当前的jdbc持久化类持久化到数据库
*/
@Bean
public JdbcTokenRepositoryImpl jdbcTokenRepository(){
// 创建对象
JdbcTokenRepositoryImpl jdbcTokenRepository=new JdbcTokenRepositoryImpl();
// 设置数据源
jdbcTokenRepository.setDataSource(dataSource);
// 自动创建表(使用过一次后请注释)
// jdbcTokenRepository.setCreateTableOnStartup(true);
return jdbcTokenRepository;
}
这里是自动创建的数据库表,主要是储存了一个token来记住当前的录的用户,通过判断当前时间是否超过最后一次最后可使用时间来判断token是否过期
last_used
添加代码在
configure(HttpSecurity http)
中,,记住密码的参数名也可以自行修改,在下面rememberMeParameter
中设置就好了
// 记住密码
http.rememberMe()
// 设置数据源
.tokenRepository(jdbcTokenRepository())
// 设置参数名
.rememberMeParameter("remember-me")
// 设置超时时间(单位秒)
.tokenValiditySeconds(60)
// 设置自定义等录逻辑
.userDetailsService(mySecurityUserDetails);
退出登录
退出登录在SpringSecurity中也可以设置,默认在浏览器上输入
localhost:8080/logout
就可以完成退出,这里是默认的退出,可以自己设置自己想要的退出
// 退出登录
http.logout()
// 自定义退出成功的url
.logoutUrl("/logOut")
// 退出成功之后的页面
.logoutSuccessUrl("login.html");
编写Controller
@Controller
public class SecurityController {
@PostMapping("/toError")
public String error(){
return "redirect:/error.html";
}
@PostMapping("/toMain")
public String main(){
return "redirect:/main.html";
}
}
登录页面
login.html
,这里讲一个知识点,在SpringSecurity中默认的用户名、密码参数是username
、password
如果要进行更改就得在配置类中进行参数名的配置,否则会登录失败如图:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登陆</title>
</head>
<body>
<h1>登陆</h1>
<form method="post" action="/login">
<div>
用户名:<input type="text" name="username">
</div>
<div>
密码:<input type="password" name="password">
</div>
<div>
<button type="submit">立即登陆</button>
</div>
</form>
</body>
</html>
登录成功的页面
main.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
登录成功
</body>
</html>
失败的页面
error.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
登录失败
</body>
</html>
启动项目
这里要注意由于没有设置注册的页面,请通过
BCryptPasswordEncoder
类加密密码再存入数据库,再进行登录,这样就可以访问登录成功的页面,密码错误也能去到错误的页面,下一章介绍登录成功之后判断用户是否具有一定的权限才能访问到特定的页面,如只有爱奇艺会员才可以看会员专属的电影