SpringSecurity系列之web权限方案

上篇:SpringSecurity系列之查询数据库认证

一、SpringSecurity系列之web权限方案

1、自定义用户登录页面

(1)自定义用户登录处理类

不需要验证就可以直接访问controller返回的信息

代码实现如下:

package org.apache.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@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();
    }

//TODO 自定义用户登录页面
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin()   //自定义自己编写的登录页面
                .loginPage("/login.html")  //登录页面设置
                .loginProcessingUrl("/user/login")   //登录访问路径
                .defaultSuccessUrl("/success.html").permitAll()  //登录成功之后,跳转路径
                .and().authorizeRequests()
                .antMatchers("/","/test/hello","/user/login").permitAll() //设置哪些路径可以直接访问,不需要认证
                .anyRequest().authenticated()
                .and().csrf().disable();  //关闭csrf防护

    }
}

(2)控制类

package org.apache.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("/test")
public class TestController {
    @GetMapping("hello")
    public String hello() {
        return "hello security";
    }

    @GetMapping("index")
    public String index() {
        return "hello index";
    }
}

 (3)静态页面

在resource文件目录下创建static文件编写login.html文件

<!DOCTYPE html>

<html  xmlns:th="http://www.thymeleaf.org">
<head lang="en">
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
    <title>xx</title>
</head>
<body>


<form action="/login"method="post">
    用户名:<input type="text"name="username"/><br/>
    密码:<input type="password"name="password"/><br/>
    <input type="submit"value="提交"/>
</form>
</body>
</html>

启动主程序类,访问:http://localhost:8111/test/hello

 2、基于角色和权限访问控制

2.1、hasAuthority 方法

如果当前的主体具有指定的权限,有返回 true,没有则返回 false

  • 在配置类设置当前访问地址有哪些权限
//表示当前登录用户具有admins权限时才可以访问这个路径
.antMatchers("/test/index").hasAuthority("admins")

当没有给权限时,我们再次登录

type=Forbidden:表示禁止访问,就是没有权限

  • 在 UserDetailsService,把返回 User对象设置权限
List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("admins");

再次访问成功看到 HelloIndex

2.2、hasAnyAuthority 方法

如果当前的主体具有在一些指定权限中是否有一个权限,有返回 true,没有则返回 false

.antMatchers("/test/index").hasAnyAuthority("admins","manage")

再次访问成功看到 Hello Index

hasAnyAuthority 方法

如果当前的主体具有在一些指定权限中是否有一个权限,有返回 true,没有则返回 false

.antMatchers("/test/index").hasAnyAuthority("admins","manage")

 Test 我们给的权限是 admins,则满足条件可以正常访问

2.3、hasRole 方法

  • 如果用户具备给定的角色就允许访问,否则出现403
  • 如果当前主体具有指定的角色,则返回true

设置访问角色

.antMatchers("/test/index").hasRole("sale")

添加访问角色

List<GrantedAuthority> auths = 
AuthorityUtils.commaSeparatedStringToAuthorityList("admins,ROLE_sale");

 添加角色时用逗号隔开,注意角色需要添加 ROLE_ 前缀

原因:源码展示

private static String hasRole(String role) {
    Assert.notNull(role, "role cannot be null"); //判断是否为null
    //在设置访问角色时如果前缀是 ROLE_ 开始,则返回false,并提示,不要自己添加前缀,因为是它自己添加的(在下面retrun中可以看出结果)
    Assert.isTrue(!role.startsWith("ROLE_"), () -> {
        return "role should not start with 'ROLE_' since it is automatically inserted. Got '" + role + "'";
    }); 
    return "hasRole('ROLE_" + role + "')";
}

所以即使我们设置的权限没有 ROLE_ 前缀,而在添加权限时我们要加上这个前缀 

2.4、hasAnyRole 方法

表示用户具备任何一个条件都可以访问

.antMatchers("/test/index").hasAnyRole("sale","develop")

3、自定义 403 界面

自定义 403 没有权限访问的页面

  • 在静态资源文件夹中创建一个准备好的页面
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>没有访问权限</h1>
</body>
</html>

设置访问配置类

 

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    /**
     * 自定义登录页面设置
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.exceptionHandling().accessDeniedPage("/unAuthority.html");
       
    }
}

当我们再次访问没有权限时,会跳转到刚才设置的界面,表示成功

4、注解的使用

4.1、@Secured

判断是否具有角色,另外需要注意的是这里匹配的字符串需要添加前缀 ROLE_

  • 使用前提,在启动类上或者配置类上添加下面注解
@EnableGlobalMethodSecurity(securedEnabled = true)
@SpringBootApplication
public class SecurityDemo1Application {

    public static void main(String[] args) {
        SpringApplication.run(SecurityDemo1Application.class, args);
    }

}

在 controller的方法上面使用注解,设置角色

@Secured({"ROLE_sale","ROLE_manage"})
@GetMapping("delete")
public String delete(){
    return "Hello Delete";
}

4.2、@PreAuthorize

注解适合进入方法前的权限验证,可以将登录用户的 roles/permissions 参数传到方法中

@PreAuthorize("hasAnyRole('admins')")
@GetMapping("delete")
public String delete(){
    return "Hello Delete";
}

4.3、@PreAuthorize

注解适合进入方法前的权限验证,可以将登录用户的 roles/permissions 参数传到方法中

@PreAuthorize("hasAnyRole('admins')")
@GetMapping("delete")
public String delete(){
    return "Hello Delete";
}

4.4、@PostAuthorize

表示方法执行后再进行校验

  • 使用前提,开启注解使用(添加在配置类或启动类上)
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)

@PostAuthorize("hasAnyRole('admins')") //方法执行之后校验
@GetMapping("delete")
public String delete(){
	System.out.println("Hello ROLE_admins");
    return "Hello Delete";
}

此时如果用户不是这个角色,那么访问后会跳转到403界面,都是还是会执行方法中的内容

Hello ROLE_admins

4.5、@PostFilter

权限验证之后对数据进行过滤

@PostFilter("filterObject.username == 'onana'")//只拿到 username为 onana 的数据,过滤掉其他数据
@PreAuthorize("hasAnyAuthority('admins')")
@GetMapping("getAll")
@ResponseBody
public List<Users> getAllUser(){
    List<Users> users = new ArrayList<>();
    users.add(new Users(null,"onana","123"));
    users.add(new Users(null,"lsisi","123"));
    return users;
}

结果

[
  {
    "id": null,
    "username": "onana",
    "password": "123"
  }
]

4.6、@PreFilter

权限验证之前对数据进行过滤

5、用户注销

(1)在配置类中设置退出的配置

http.logout()
    .logoutUrl("/logout") //设置退出的请求地址
    .logoutSuccessUrl("/test/hello").permitAll(); //退出完成后跳转页面

(2)添加一个退出的请求路径

<a href="/logout">退出</a>

登录成功后跳转到 成功界面,然后可以正常访问有权限的地址,点击退出后再进行访问则需要登陆

6、自动登录(记住我)

实现原理:

(1)首先用户登入成功后,会生成一个 token(加密串),这个 token,一边相应给浏览器,放到 cookie中,一边使用 token 和用户信息 存储到数据库中,

(2)而以后再次访问时,浏览器获取 cookie信息,拿着cookie信息到数据库进行比对,如果查询到对应信息,则认证成功,实现自动登录
 

6.1、建表 

DROP TABLE IF EXISTS `persistent_logins`;
CREATE TABLE `persistent_logins`  (
  `username` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `series` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `token` varchar(64) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `last_used` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`series`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

6.2、配置类

注入数据源,配置操作数据库对象

@Configuration
public class SecurityConfiguration 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;
    }

}

在配置类的 configure方法中配置自动登录

http.formLogin()
    .and().rememberMe().tokenRepository(persistentTokenRepository())
    .tokenValiditySeconds(60) //设置有效时长(s)
    .userDetailsService(userDetailsService)

在登录页面中添加一个复选框

<div>
    <lable>记住我:</lable>
    <input type="checkbox" name="remember-me"> <!--这里的name="remember-me" 是必须的   -->
</div>

当我们登入成功后,可以看到 security为我们自动封装的数据已经储存到数据库中了

 

7、CSRF

csrf指的是:跨站请求伪造(Cross-site request forgery),也被称为one-click attack 或者 session riding 通常缩写为 CSRF 或者 XSRF,是一种挟制用户在当前已经登录的 Web 应用程序上执行非本意的操作的攻击方法。跟跨网站脚本(XSS)相比,XSS 利用的是用户对指定网站的信任,CSRF利用的是网站对用户网页浏览器的信任。

​ 跨站请求攻击,简单地说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并运行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去运行。这利用了 web中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是不是用户自愿发出的

<!-- 使用模板引擎 -->
<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />

实现原理:

在session中存放一个 token,保护 post,put,delete 请求,判断提交的请求中 token与session 中的是否一致,相同则放行

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Security是一个用于实现身份验证和授权的框架,它提供了多种权限管理的方式。在Spring Security中,常见的权限控制方式有四种:\[1\] 1. 基于角色的权限控制:通过给用户分配不同的角色,然后在系统中定义角色与权限的对应关系,来控制用户对资源的访问权限。 2. 基于URL的权限控制:通过配置URL的访问规则,来限制用户对不同URL的访问权限。 3. 基于方法的权限控制:通过在方法上添加注解或配置,来限制用户对方法的访问权限。 4. 自定义权限控制:Spring Security还提供了自定义权限控制的方式,可以根据具体业务需求来实现特定的权限控制逻辑。 这些权限控制方式可以根据实际需求进行组合使用,以实现灵活的权限管理。Spring SecuritySpring家族中的一员,它基于Spring框架,提供了一套完整的Web应用安全解决方案。\[2\]\[3\] #### 引用[.reference_title] - *1* *2* [Spring教程之Spring Security的四种权限控制方式](https://blog.csdn.net/qfxulei/article/details/120999389)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [SpringSecurity权限控制](https://blog.csdn.net/qq_61544409/article/details/129685347)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值