IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Spring Security -> 正文阅读

[Java知识库]Spring Security

框架解决问题

本质上是过滤器链,使用这些过滤器解决:
1、用户认证
检验用户是否为系统中的合法主体。

2、用户授权
检验某位用户是否有权限执行某项操作。

使用

一般会搭配SpringBoot使用

依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
	<version>{spring-security-version}</version>
</dependency>

初步使用

写一个简单的controller,直接返回String 就行
这时候去登录,在前端网页会出现要登录的页面
Spring Security默认用户user,默认生成随机密码在控制台

自定义开发

用户名+密码

自定义类继承UsernamePasswordAuthenticationFilter过滤器,重写attemptAuthentication、successfulAuthentication、unsuccessfulAuthentication方法。
在attemptAuthentication方法中做校验,成功则调用successfulAuthentication方法、失败则调用unsuccessfulAuthentication方法。

注意 做认证差数据库的操作,需要写在实现UserDetailsService接口的类中

步骤

1、创建类继承UsernamePasswordAuthenticationFilter,重写三个方法。
2、创建类实现UserDetailsService接口,编写查询数据过程,返回User对象(这里的User对象是安全框架的)

当返回的User对象密码数据需要加密的时候:使用PasswordEncoder接口:
new一个BCryptPasswordEncoder,使用它的encode方法完成加密。
还需要在@Configuration+@Bean注册一个BCryptPasswordEncoder实例对象进Spring容器

示例:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{
	@Autowired
	private UserDetailsService userDetailsService;

	@Override
	protected void configure(AuthenticationManagerBuilder auth)throws Exception{
		auth.userDetailsService(userDetailsService).passwordEncoder(password());
	}

	@Bean
	PasswordEncoder password(){return new BCryptPasswordEncoder();}
}
@Service
public class LoginService implements UserDetailsService {
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		// 判断用户名是否存在
		if (!"admin".equals(username)){
			throw new UsernameNotFoundException("用户名不存在!");
		}
		// 从数据库中获取的密码 atguigu 的密文
		String pwd ="$2a$10$2R/M6iU3mCZt3ByG7kwYTeeW0w7/UqdeXrb27zkBIizBvAven0/na"; 
		return new User(username,pwd, AuthorityUtils.commaSeparatedStringToAuthorityList("admin,"));// 第三个参数表示权限
	}
}

示例

从数据库中查用户名和密码,来完成登录校验

依赖:SpringBoot、Spring Security、MyBatisPlus、MySQL、lombok

配置文件application.properties

#mysql 数据库连接
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/demo?serverTimezone=GMT%2B8 spring.datasource.username=root
spring.datasource.password=root

实体类entity

@Data
public class Users {
    private Integer id;
    private String username;
    private String password;
}

登录实现类Service

@Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {
	@Autowired
	private UsersMapper usersMapper;
	@Override
	public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
		QueryWrapper<Users> wrapper = new QueryWrapper(); 
		wrapper.eq("username",s);
		Users users = usersMapper.selectOne(wrapper);
		if(users == null) {
			throw new UsernameNotFoundException("用户名不存在!");
		}
		System.out.println(users);
		List<GrantedAuthority> auths =AuthorityUtils.commaSeparatedStringToAuthorityList("admin,ROLE_sale"); 
		return new User(users.getUsername(),new BCryptPasswordEncoder().encode(users.getPassword()),auths);
	}
}

配置类

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{
	@Autowired
	private UserDetailsService userDetailsService;

	@Override
	protected void configure(AuthenticationManagerBuilder auth)throws Exception{
		auth.userDetailsService(userDetailsService).passwordEncoder(password());
	}

	@Bean
	PasswordEncoder password(){return new BCryptPasswordEncoder();}
}

配置需要认证的路径/登录页面

配置类修改为如下:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{
	@Autowired
	private UserDetailsService userDetailsService;

	@Override
	protected void configure(AuthenticationManagerBuilder auth)throws Exception{
		auth.userDetailsService(userDetailsService).passwordEncoder(password());
	}

	@Bean
	PasswordEncoder password(){return new BCryptPasswordEncoder();}

	//设置登录页面转到自定义页面
	@Override
	protected void configure(HttpSecurity http)throws Exception{
		http.formLogin() //自定义自己编写的登录页面
			.loginPage("/login.html") //登录页面(当访问需要认证的页面时,会跳转到这个页面)
			.loginProcessingUrl("/user/login") //登录表单路径,就是登录操作触发哪个controller,不需要自己实现,随便写一个
			.defaultSuccessUrl("/success").permitAll() //登录成功之后转到的路径
			.and().authorizeRequests()
				.antMatchers("/","/test/hello","/user/login").permitAll()//访问这些路径不需要认证
				.anyRequest().authenticated()
			.and().csrf().desable();//关闭csrf防护
	}
}

用户授权

基于角色或权限进行访问控制

hasAuthority方法

当前主体有权限时返回true 无权限返回false

配置类

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{
	@Autowired
	private UserDetailsService userDetailsService;

	@Override
	protected void configure(AuthenticationManagerBuilder auth)throws Exception{
		auth.userDetailsService(userDetailsService).passwordEncoder(password());
	}

	@Bean
	PasswordEncoder password(){return new BCryptPasswordEncoder();}

	//设置登录页面转到自定义页面
	@Override
	protected void configure(HttpSecurity http)throws Exception{
		http.formLogin() //自定义自己编写的登录页面
			.loginPage("/login.html") //登录页面(当访问需要认证的页面时,会跳转到这个页面)
			.loginProcessingUrl("/user/login") //登录表单路径,就是登录操作触发哪个controller,不需要自己实现,随便写一个
			.defaultSuccessUrl("/success").permitAll() //登录成功之后转到的路径
			.and().authorizeRequests()
				.antMatchers("/","/test/hello","/user/login").permitAll()//访问这些路径不需要认证
				.andMatchers("/test/index").hasAuthority("admin")//用户需要带有admin权限
				.anyRequest().authenticated()
			.and().csrf().desable();//关闭csrf防护
	}
}

hasAnyAuthority方法

当前的主体有任何提供的角色(给定的作为一个逗号分隔的字符串列表)的话,返回true
配置类

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{
	@Autowired
	private UserDetailsService userDetailsService;

	@Override
	protected void configure(AuthenticationManagerBuilder auth)throws Exception{
		auth.userDetailsService(userDetailsService).passwordEncoder(password());
	}

	@Bean
	PasswordEncoder password(){return new BCryptPasswordEncoder();}

	//设置登录页面转到自定义页面
	@Override
	protected void configure(HttpSecurity http)throws Exception{
		http.formLogin() //自定义自己编写的登录页面
			.loginPage("/login.html") //登录页面(当访问需要认证的页面时,会跳转到这个页面)
			.loginProcessingUrl("/user/login") //登录表单路径,就是登录操作触发哪个controller,不需要自己实现,随便写一个
			.defaultSuccessUrl("/success").permitAll() //登录成功之后转到的路径
			.and().authorizeRequests()
				.antMatchers("/","/test/hello","/user/login").permitAll()//访问这些路径不需要认证
				.andMatchers("/test/index").hasAnyAuthority("admin,manager")//用户需要带有admin权限
				.anyRequest().authenticated()
			.and().csrf().desable();//关闭csrf防护
	}
}

hasRole方法

如果用户具备给定角色就允许访问,否则出现403。类似hasAuthority方法。
对应部分改为:.andMatchers("/test/index").hasRole("admin")//用户需要带有admin权限

注意不同点在于,userDetailService返回的角色需要ROLE_xxx(有前缀),而hasRole方法参数则为xxx。

hasAnyRole方法

类似hasAnyAuthority方法。

配置无权限时跳转页面

配置类

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{
	@Autowired
	private UserDetailsService userDetailsService;

	@Override
	protected void configure(AuthenticationManagerBuilder auth)throws Exception{
		auth.userDetailsService(userDetailsService).passwordEncoder(password());
	}

	@Bean
	PasswordEncoder password(){return new BCryptPasswordEncoder();}

	//设置登录页面转到自定义页面
	@Override
	protected void configure(HttpSecurity http)throws Exception{
		http.exceptionHandling().accessDeniedPage("/unauth.html");//设置无权限访问跳转自定义页面
		http.formLogin() //自定义自己编写的登录页面
			.loginPage("/login.html") //登录页面(当访问需要认证的页面时,会跳转到这个页面)
			.loginProcessingUrl("/user/login") //登录表单路径,就是登录操作触发哪个controller,不需要自己实现,随便写一个
			.defaultSuccessUrl("/success").permitAll() //登录成功之后转到的路径
			.and().authorizeRequests()
				.antMatchers("/","/test/hello","/user/login").permitAll()//访问这些路径不需要认证
				.andMatchers("/test/index").hasAnyAuthority("admin,manager")//用户需要带有admin权限
				.anyRequest().authenticated()
			.and().csrf().desable();//关闭csrf防护
	}
}

用户授权(注解使用)

@Secured

判断是否具有角色,另外需要注意的是这里匹配的字符串需要添加前缀“ROLE_“。 使用注解先要开启注解功能,在 启动类或配置类 上添加@EnableGlobalMethodSecurity(securedEnabled=true)注解

在controller层的方法上面加上@Secured注解

@RequestMapping("testSecured")
@ResponseBody 
@Secured({"ROLE_normal","ROLE_admin"}) //需要ROLE_前缀开头
public String helloUser() {
	return "hello,user";
}

@PreAuthorize

注解适合进入方法前的权限验证, @PreAuthorize 可以将登录用户的 roles/permissions 参数传到方法中。使用注解先要开启注解功能,在 启动类或配置类 添加@EnableGlobalMethodSecurity(prePostEnabled = true)

在controller层的方法上面加上@PreAuthorize注解

@RequestMapping("/preAuthorize") @ResponseBody
//@PreAuthorize("hasRole('ROLE_管理员')")
@PreAuthorize("hasAnyAuthority('menu:system')")//注意引号,hasAnyAuthority这里也可以使用另外三个
public String preAuthorize(){
	System.out.println("preAuthorize"); return "preAuthorize";
}

@PostAuthorize

注解使用并不多,在方法执行后再进行权限验证,适合验证带有返回值 的权限。启动类开启@EnableGlobalMethodSecurity(prePostEnabled = true)

在controller层的方法上面加上@PostAuthorize注解

@RequestMapping("/testPostAuthorize") 
@ResponseBody @PostAuthorize("hasAnyAuthority('menu:system')") 
public String preAuthorize(){
	System.out.println("test--PostAuthorize"); return "PostAuthorize";
}

@PostFilter

权限验证之后对返回前端的数据进行过滤,留下用户名是 admin1 的数据,表达式中的 filterObject 引用的是方法返回值 List 中的某一个元素

controller层方法

@RequestMapping("getAll") 
@PreAuthorize("hasRole('ROLE_管理员')") 
@PostFilter("filterObject.username == 'admin1'")
@ResponseBody
public List<UserInfo> getAllUser(){
	ArrayList<UserInfo> list = new ArrayList<>(); 
	list.add(new UserInfo(1l,"admin1","6666")); 
	list.add(new UserInfo(2l,"admin2","888"));
	return list;
}

@PreFilter

进入控制器之前对前端传入数据进行过滤

controller层方法

@RequestMapping("getTestPreFilter") 
@PreAuthorize("hasRole('ROLE_管理员')") 
@PreFilter(value = "filterObject.id%2==0") 
@ResponseBody
public List<UserInfo> getTestPreFilter(@RequestBody List<UserInfo> list){
	list.forEach(t-> { System.out.println(t.getId()+"\t"+t.getUsername());
	});
	return list;
}

用户注销

用户登录后如何进行退出操作,需要在配置类中添加退出映射地址http.logout().logoutUrl("/logout").logoutSuccessUrl("/index").permitAll()

配置类

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter{
	@Autowired
	private UserDetailsService userDetailsService;

	@Override
	protected void configure(AuthenticationManagerBuilder auth)throws Exception{
		auth.userDetailsService(userDetailsService).passwordEncoder(password());
	}

	@Bean
	PasswordEncoder password(){return new BCryptPasswordEncoder();}

	//设置登录页面转到自定义页面
	@Override
	protected void configure(HttpSecurity http)throws Exception{
		//设置退出
		http.logout().logoutUrl("/logout").logoutSuccessUrl("/index").permitAll();
		
		http.exceptionHandling().accessDeniedPage("/unauth.html");//设置无权限访问跳转自定义页面
		http.formLogin() //自定义自己编写的登录页面
			.loginPage("/login.html") //登录页面(当访问需要认证的页面时,会跳转到这个页面)
			.loginProcessingUrl("/user/login") //登录表单路径,就是登录操作触发哪个controller,不需要自己实现,随便写一个
			.defaultSuccessUrl("/success").permitAll() //登录成功之后转到的路径
			.and().authorizeRequests()
				.antMatchers("/","/test/hello","/user/login").permitAll()//访问这些路径不需要认证
				.andMatchers("/test/index").hasAnyAuthority("admin,manager")//用户需要带有admin权限
				.anyRequest().authenticated()
			.and().csrf().desable();//关闭csrf防护
	}
}

自动登录

实现效果:需要认证才能访问的页面,在关闭浏览器再次打开后还能不能录直接访问。

原理:把cookie和对应用户信息存入数据库

重点

单点登录

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-04-18 17:27:33  更:2022-04-18 17:30:51 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 4:46:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码