02 Web 权限方案
Web 权限方案
用户认证
设置登录系统的账号、密码
方式一:在 application.properties
spring.security.user.name=uzi
spring.security.user.password=123
方式二:配置类实现接口
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//对密码进行加密
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String encode = passwordEncoder.encode("123");
auth.inMemoryAuthentication().withUser("uzi").password(encode).roles("admin");
}
@Bean
PasswordEncoder password(){
return new BCryptPasswordEncoder();
}
}
方式三:编写类实现接口
@Configuration
public class SecurityConfigTest 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("UserDetailsService")
public class MyUserDetails implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("role");
return new User("uzi",new BCryptPasswordEncoder().encode("123"),auths);
}
}
实现数据库认证来完成用户登录
- 引入 MyBatis-Plus MySQL 依赖
- User 的实体类
- 整合 MybatisPlus 制作 mapper
@Service
public class MyUserDetails implements UserDetailsService {
@Autowired
private UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//根据用户名 查询数据库
QueryWrapper<Users> QueryWrapper = new QueryWrapper<Users>();
QueryWrapper.eq("username",username);
Users users = userMapper.selectOne(QueryWrapper);
//判断
if (users == null) {
throw new UsernameNotFoundException("用户名不存在!");
}
List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("role");
//从 users 对象里面 得到用户名和密码
return new User(users.getUsername(),
new BCryptPasswordEncoder().encode(users.getPassword()),auths);
}
}
@Configuration
public class SecurityConfigTest 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 SecurityConfigTest 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 由SpringSecurity完成
.defaultSuccessUrl("/test/index").permitAll() //登陆成功之后 跳转路径
.and().authorizeRequests()
.antMatchers("/","/test/hello","user/login").permitAll() //设置那些路径能够直接访问,不需要认证
.anyRequest().authenticated()
.and().csrf().disable();//关闭 csrf 防护
}
}
controller 控制器
@RestController
@RequestMapping("test")
public class TestController {
@GetMapping("hello")
public String hello(){
return "hello security";
}
@GetMapping("index")
public String index(){
return "hello index";
}
}
login.html
<form action="/login"method="post">
用户名:<input type="text"name="username"/><br/>
密码:<input type="password"name="password"/><br/>
<input type="submit"value="提交"/>
</form>
注意:页面提交方式必须为 post 请求,所以上面的页面不能使用,用户名,密码必须为username,password
用户授权
1.1 基于用户或权限进行访问控制
1.1.1 hasAuthority() 方法
如果当前的主体具有指定的权限,则返回true,否则返回false(一个权限)
- 在配置类设置当前访问地址有哪些权限
//表示当前登录用户 只有admins 权限 才能访问这个路径 .antMatchers("/test/index").hasAuthority("admins")
- 在 UserDetailsService ,把返回 users 对象设置权限
List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("admins");
测试: 如果返回403 就是没有权限
1.1.2 hasAnyAuthority() 方法
如果当前的主体具有其中的某一个权限,则返回true,否则返回false
.antMatchers("/test/index").hasAnyAuthority("admins,manager")
1.1.3 hasRole() 方法
如果用户具备给定角色就允许访问,否则出现 403。
如果当前主体具有指定的角色,则返回 true。
// 3. hasRole() 方法 源码变成 ROLE_sale
.antMatchers("/test/index").hasRole("sale")
在 UserDetailsService ,把返回 users 对象设置权限
List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("admins,ROLE_sale");
1.1.4 hasAnyRole() 方法
表示用户具有任何一个条件都可以访问
自定义403 没有权限访问页面
@Configuration
public class SecurityConfigTest 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 由SpringSecurity完成
.defaultSuccessUrl("/test/index").permitAll() //登陆成功之后 跳转路径
.and().authorizeRequests()
.antMatchers("/","/test/hello","user/login").permitAll() //设置那些路径能够直接访问,不需要认证
//表示当前登录用户 只有admins 权限 才能访问这个路径
//1.hasAuthority() 方法
//.antMatchers("/test/index").hasAuthority("admins")
//2.hasAnyAuthority() 方法
//.antMatchers("/test/index").hasAnyAuthority("admins,manager")
// 3. hasRole() 方法 源码变成 ROLE_sale
.antMatchers("/test/index").hasRole("sale")
.anyRequest().authenticated()
.and().csrf().disable();//关闭 csrf 防护
}
}
认证授权(注解使用)
@Secured
用户具有某个角色 可以访问方法
- 在 启动类(配置类) 开启注解
@EnableGlobalMethodSecurity(securedEnabled = true)
- 在 controller的方法上面使用注解,开启角色
@GetMapping("update")
@Secured({"ROLE_sale","ROLE_manager"})
public String update(){
return "hello update";
}
- 在 UserDetailsService ,把返回 users 对象设置角色
List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("admins,ROLE_sale");
@PreAuthorize
进入方法前做的权限认证
- 在 启动类(配置类) 开启注解
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
- 在 controller的方法上面使用注解
@GetMapping("update")
//@Secured({"ROLE_sale","ROLE_manager"})
@PreAuthorize("hasAnyAuthority('admins')")//hasAuthority 等等
public String update(){
return "hello update";
}
@PostAuthorize
进入方法之后做的权限认证
- 在 启动类(配置类) 开启注解
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
@PreFilter
进入控制器之前对数据进行过滤
@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;
}
@PostFilter
进入控制器之后对数据进行过滤
用户注销
- 在配置类添加退出的配置类
@Override
protected void configure(HttpSecurity http) throws Exception {
//退出
http.logout().logoutUrl("/logout").logoutSuccessUrl("/test/hello").permitAll();
自动登录
@Configuration
public class SecurityConfigTest extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private DataSource dataSource;
//注入数据源,配置操作数据库对象
@Bean
public PersistentTokenRepository persistentTokenRepository(){
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
jdbcTokenRepository.setCreateTableOnStartup(true);
return jdbcTokenRepository;
}
@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("/test/hello").permitAll();
//配置没有权限访问跳转自定义页面
http.exceptionHandling().accessDeniedPage("/unauth.html");
//自定义自己编写的登录页面
http.formLogin()
.loginPage("/login.html")// 登录页面
.loginProcessingUrl("/user/login") //登录访问路径 登录之后交给哪个 controller 由SpringSecurity完成
.defaultSuccessUrl("/success.html").permitAll() //登陆成功之后 跳转路径
.and().authorizeRequests()
.antMatchers("/","/test/hello","user/login").permitAll() //设置那些路径能够直接访问,不需要认证
//表示当前登录用户 只有admins 权限 才能访问这个路径
//1.hasAuthority() 方法
// .antMatchers("/test/index").hasAuthority("admins")
//2.hasAnyAuthority() 方法
//.antMatchers("/test/index").hasAnyAuthority("admins,manager")
// 3. hasRole() 方法 源码变成 ROLE_sale
.antMatchers("/test/index").hasRole("sale")
.anyRequest().authenticated()
.and().rememberMe().tokenRepository(persistentTokenRepository())
.tokenValiditySeconds(60)//设置有效时常
.userDetailsService(userDetailsService)
.and().csrf().disable();//关闭 csrf 防护
}
}
CSRF 理解
跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click
attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的 Web 应用程序上执行非本意的操作的攻击方法。跟跨网站脚本(XSS)相比,XSS利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。
从 Spring Security 4.0 开始,默认情况下会启用 CSRF 保护,以防止 CSRF 攻击应用程序,Spring Security CSRF 会针对 PATCH,POST,PUT 和 DELETE 方法进行防护。
在登录页面添加一个隐藏域:
<input
type="hidden"th:if="${_csrf}!=null"th:value="${_csrf.token}"name="_csrf
"/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)