网站首页 > 技术文章 正文
SpringSecurity Web 权限方案
(一)设置登录系统的账号、密码
1. 配置文件application.properties
#配置文件配置用户名密码
spring.security.user.name=lisi
spring.security.user.password=123
2. 编写配置类实现
@Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
String pass = encoder.encode("123");
auth.inMemoryAuthentication().withUser("hello").password(pass).roles("admin");
}
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
3. 实现UserDetailsService实现
config文件:
|
4. 实现数据库认证来完成用户登录
(1) 建数据脚本
create table users(
id bigint primary key auto_increment,
username varchar(20) unique not null,
password varchar(100)
);
(2) Pom依赖
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--lombok 用来简化实体类-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
(3) 配置文件
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=root
(4) 实体类
@Data
public class Users {
private Integer id;
private String username;
private String password;
}
(5) 整合 MybatisPlus 制作 mapper
@Repository
public interface UserMapper extends BaseMapper<Users> {
}
(6) 登录实现类
@Service("userDetailsService")
public class UserService implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
QueryWrapper<Users> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username",s);
Users users = userMapper.selectOne(queryWrapper);
if(users == null){
throw new UsernameNotFoundException("用户不存在");
}
List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("admin,ROLE_sale");
return new User(users.getUsername(),new BCryptPasswordEncoder().encode(users.getPassword()),auths);
}
}
(7) 访问
http://localhost:8080/test/demo1输入同户名密码
5. 自定义登录页面
(1) 编写页面
<body>
<form action="/user/login" method="post">
<label>用户名:</label><input type="text" name="username">
<br>
<label>密码:</label><input type="password" name="password">
<br>
<input type="submit">
</form>
</body>
(2) 编写配置类
@Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/login.html")//自定义自己编写的登录页面
.loginProcessingUrl("/user/login") //登录访问路径
.and().authorizeRequests()
.antMatchers("/","/user/login","/login.html").permitAll()//设置路径不需要认证可以直接访问
.anyRequest().authenticated()
.and().csrf().disable();//关闭csrf防护
}
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
(3) 其他配置看第4点
(4) 访问
注意:页面提交方式必须为 post 请求,所以上面的页面不能使用,用户名,密码必须为username,password
6. 自定义403错误页面
(1) 编写页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>err403</title>
</head>
<body>
<h1>无权限访问</h1>
</body>
</html>
(2) 编写配置类
@Override
protected void configure(HttpSecurity http) throws Exception {
//配置无权限访问页面
http.exceptionHandling().accessDeniedPage("/err.html");
http.formLogin()
.loginPage("/login.html")//自定义自己编写的登录页面
.loginProcessingUrl("/user/login") //登录访问路径
.and().authorizeRequests()
.antMatchers("/","/user/login","/login.html").permitAll()//设置路径不需要认证可以直接访问
// .antMatchers("/test/demo1").hasAuthority("admin")
// .antMatchers("/test/demo1").hasAnyAuthority("admin,create")
// .antMatchers("/test/demo1").hasRole("sale")
.antMatchers("/test/demo1").hasAnyRole("sale1")
.anyRequest().authenticated()
.and().csrf().disable();//关闭csrf防护
}
(3) 访问
http://localhost:8080/test/demo1
(二)基于角色或权限进行访问控制
(1) hasAuthority 方法
如果当前的主体具有指定的权限,则返回 true,否则返回 false
配置类:
赋予用户权限:
|
访问成功:
访问失败:
(2) hasAnyAuthority 方法
如果当前的主体有任何提供的角色(给定的作为一个逗号分隔的字符串列表)的话,返回true.
......
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/login.html")//自定义自己编写的登录页面
.loginProcessingUrl("/user/login") //登录访问路径
.and().authorizeRequests()
.antMatchers("/","/user/login","/login.html").permitAll()//设置路径不需要认证可以直接访问
// .antMatchers("/test/demo1").hasAuthority("admin")
.antMatchers("/test/demo1").hasAnyAuthority("admin,create")
.anyRequest().authenticated()
.and().csrf().disable();//关闭csrf防护
}
......
(3) hasRole 方法
如果用户具备给定角色就允许访问,否则出现 403。
如果当前主体具有指定的角色,则返回 true。
底层源码:(底层拼接了ROLE_)
private static String hasRole(String role) {
Assert.notNull(role, "role cannot be null");
Assert.isTrue(!role.startsWith("ROLE_"), () -> {
return "role should not start with 'ROLE_' since it is automatically inserted. Got '" + role + "'";
});
return "hasRole('ROLE_" + role + "')";
}
给用户添加角色:
配置类:
赋予用户角色:
|
注意配置文件中不需要添加”ROLE_“,因为上述的底层代码会自动添加与之进行匹配。
(4) hasAnyRole
表示用户具备任何一个条件都可以访问。
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()
.loginPage("/login.html")//自定义自己编写的登录页面
.loginProcessingUrl("/user/login") //登录访问路径
.and().authorizeRequests()
.antMatchers("/","/user/login","/login.html").permitAll()//设置路径不需要认证可以直接访问
// .antMatchers("/test/demo1").hasAuthority("admin")
// .antMatchers("/test/demo1").hasAnyAuthority("admin,create")
// .antMatchers("/test/demo1").hasRole("sale")
.antMatchers("/test/demo1").hasAnyRole("sale,sale1")
.anyRequest().authenticated()
.and().csrf().disable();//关闭csrf防护
}
(三)注解使用
(1) @Secured
判断是否具有角色,需要注意的是这里匹配的字符串需要添加前缀“ROLE_“。
使用注解先要开启注解功能:EnableGlobalMethodSecurity(securedEnabled=true)
@SpringBootApplication
@MapperScan("com.hk.springsecuritydemo.dao")
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SpringsecuritydemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringsecuritydemoApplication.class, args);
}
}
控制层:
@RestController
@RequestMapping("/test")
public class TestController {
@RequestMapping("/demo1")
public String demo1(){
return "hello security";
}
@RequestMapping("/login")
public String login(){
return "success";
}
@RequestMapping("/demo2")
@Secured({"ROLE_sale2","ROLE_management"})
public String demo2(){
return "demo2";
}
}
当主体有sale2或者management角色时才有权限访问/demo2接口
例如现在主体有sale角色:
@Service("userDetailsService")
public class UserService implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
QueryWrapper<Users> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username",s);
Users users = userMapper.selectOne(queryWrapper);
if(users == null){
throw new UsernameNotFoundException("用户不存在");
}
List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("admin,ROLE_sale");
return new User(users.getUsername(),new BCryptPasswordEncoder().encode(users.getPassword()),auths);
}
}
访问:
http://localhost:8080/test/demo2
(2) @PreAuthorize
先开启注解功能: @EnableGlobalMethodSecurity(prePostEnabled = true)
@PreAuthorize:注解适合进入方法前的权限验证,可以将登录用户的 roles/permissions 参数传到方法中。
@SpringBootApplication
@MapperScan("com.hk.springsecuritydemo.dao")
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class SpringsecuritydemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringsecuritydemoApplication.class, args);
}
}
控制层:
@RequestMapping("/demo3")
@PreAuthorize("hasRole('ROLE_sale')")
public String demo3(){
return "demo3";
}
访问:
(3) @PostAuthorize
先开启注解功能: @EnableGlobalMethodSecurity(prePostEnabled = true)
@PostAuthorize 注解使用并不多,在方法执行后再进行权限验证,适合验证带有返回值的权限.
@SpringBootApplication
@MapperScan("com.hk.springsecuritydemo.dao")
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class SpringsecuritydemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringsecuritydemoApplication.class, args);
}
}
控制层:
@RequestMapping("/demo4")
@PostAuthorize("hasAuthority('admin')")
public String demo4(){
System.out.println("demo4");
return "demo4";
}
(4) @PostFilter
@PostFilter :权限验证之后对数据进行过滤留下用户名是 admin1 的数据;表达式中的 filterObject 引用的是方法返回值 List 中的某一个元素
@RequestMapping("/demo5")
@PostAuthorize("hasAuthority('admin')")
@PostFilter("filterObject.username == 'admin1'")
public ArrayList<Users> demo5(){
ArrayList<Users> list = new ArrayList<>();
list.add(new Users(1,"admin1","6666"));
list.add(new Users(2,"admin2","888"));
return list;
}
访问:http://localhost:8080/test/demo5
(5) @PreFilter
@PreFilter: 进入控制器之前对数据进行过滤
@RequestMapping("getTestPreFilter")
@PreAuthorize("hasRole('ROLE_sale')")
@PreFilter(value = "filterObject.id%2==0")
public List<Users> getTestPreFilter(@RequestBody List<Users> list){
list.forEach(t-> {
System.out.println(t.getId()+"\t"+t.getUsername());
});
return list;
}
Postman调用
(四)基于数据库的记住我
(1) 创建表
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) 编写配置类
@Configuration
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private DataSource dataSource;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//配置无权限访问页面
http.exceptionHandling().accessDeniedPage("/err.html");
http.formLogin()
.loginPage("/login.html")//自定义自己编写的登录页面
.loginProcessingUrl("/user/login") //登录访问路径
.and().authorizeRequests()
.antMatchers("/","/user/login","/login.html").permitAll()//设置路径不需要认证可以直接访问
// .antMatchers("/test/demo1").hasAuthority("admin")
// .antMatchers("/test/demo1").hasAnyAuthority("admin,create")
// .antMatchers("/test/demo1").hasRole("sale")
// .antMatchers("/test/demo1").hasAnyRole("sale")
.anyRequest().authenticated()
.and().csrf().disable();//关闭csrf防护
//配置记住我
http.rememberMe()
.rememberMeParameter("remember-me") // 修改请求参数名。 默认是remember-me
.tokenValiditySeconds(10) // 设置记住我有效时间。单位是秒。默认是14天
.rememberMeCookieName("remember-me") // 修改remember me的cookie名称。默认是remember-me
.tokenRepository(persistentTokenRepository()) // 配置用户登录标记的持久化工具对象。
.userDetailsService(userDetailsService); // 配置自定义的UserDetailsService接口实现类对象
}
@Bean
PersistentTokenRepository persistentTokenRepository(){
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
// jdbcTokenRepository.setCreateTableOnStartup(true);//自动建表
return jdbcTokenRepository;
}
@Bean
PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
(3) 修改登录页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body>
<form action="/user/login" method="post">
<label>用户名:</label><input type="text" name="username">
<br>
<label>密码:</label><input type="password" name="password">
<br>
<input type="checkbox" name="remember-me"> 10秒免登录
<br>
<input type="submit">
</form>
</body>
</html>
(五)用户注销
(1) 在登录成功页面添加一个退出连接
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>登录成功</h2>
<a href="/logout">退出</a>
</body>
</html>
(2) 编写配置类
......
@Override
protected void configure(HttpSecurity http) throws Exception {
//配置无权限访问页面
http.exceptionHandling().accessDeniedPage("/err.html");
//配置退出登录
http.logout().logoutUrl("/logout").logoutSuccessUrl("/login.html");
http.formLogin()
.loginPage("/login.html")//自定义自己编写的登录页面
.loginProcessingUrl("/user/login") //登录访问路径
.defaultSuccessUrl("/success.html").permitAll()//登录成功之后,跳转路径
......
结语:
每当黎明的太阳升起,美好的一天又开始了。
回复SpringSecurity,可以获得全套笔记
猜你喜欢
- 2024-09-29 Spring Security 自定义登录过程(非前后端分离版本)
- 2024-09-29 基于spring-security图形验证码、token验证
- 2024-09-29 SpringBoot 实现自动登录时的安全风险控制
- 2024-09-29 springboot+security框架整合 springboot security详解
- 2024-09-29 时序图说明JWT用户认证及接口鉴权的细节
- 2024-09-29 Spring Security 整合OAuth2 springsecurity整合oauth2+jwt+vue
- 2024-09-29 有关springboot + spring security的思考
- 2024-09-29 SpringSecurity之自定义用户权限信息的存取
- 2024-09-29 你还不了解SpringSecurity吗?快来看看SpringSecurity实战总结
- 2024-09-29 SpringSecurity和JWT实现认证和授权
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- oraclesql优化 (66)
- 类的加载机制 (75)
- feignclient (62)
- 一致性hash算法 (71)
- dockfile (66)
- 锁机制 (57)
- javaresponse (60)
- 查看hive版本 (59)
- phpworkerman (57)
- spark算子 (58)
- vue双向绑定的原理 (68)
- springbootget请求 (58)
- docker网络三种模式 (67)
- spring控制反转 (71)
- data:image/jpeg (69)
- base64 (69)
- java分页 (64)
- kibanadocker (60)
- qabstracttablemodel (62)
- java生成pdf文件 (69)
- deletelater (62)
- com.aspose.words (58)
- android.mk (62)
- qopengl (73)
- epoch_millis (61)
本文暂时没有评论,来添加一个吧(●'◡'●)