三、SrpingSecuriyt web 权限方案

3.SrpingSecuriyt web 权限方案

认证

3.1设置登录的用户名和密码

3.1.1通过配置文件

image-20221019130220765

3.1.2通过配置类

创建SecurityConfig类继承WebSecurityConfigurerAdapter重写configure方法

package com.example.demo.config;

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.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        String passWord = bCryptPasswordEncoder.encode("wx");
        auth.inMemoryAuthentication().withUser("root").password(passWord).roles("admin");
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }
}

3.1.3自定义编写实现类
  1. 创建配置类,设置使用那个UserDetailsServices实现类

    package com.example.demo.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.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(passwordEncoder());
        }
    
        @Bean
        public PasswordEncoder passwordEncoder(){
            return new BCryptPasswordEncoder();
        }
    }
    
    
  2. 编写UserDetailsService实现类,返回User对象,User对象有用户名密码和操作权限

package com.example.demo.service;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.List;

@Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        List<GrantedAuthority> role = AuthorityUtils.commaSeparatedStringToAuthorityList("role");
        return new User("root",new BCryptPasswordEncoder().encode("wx"),role);
    }
}

3.2实现数据库认证来完成用户登录

3.2.1 导入相关的依赖
        <!--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.2.2创建数据库和数据库表
CREATE TABLE users ( id BIGINT PRIMARY KEY auto_increment, username VARCHAR ( 20 ) UNIQUE NOT NULL, PASSWORD VARCHAR ( 100 ) );-- 密码 atguigu
INSERT INTO users
VALUES
	( 1, '张san', '$2a$10$2R/M6iU3mCZt3ByG7kwYTeeW0w7/UqdeXrb27zkBIizBvAven0/na' );-- 密码 atguigu
INSERT INTO users
VALUES
	( 2, '李si', '$2a$10$2R/M6iU3mCZt3ByG7kwYTeeW0w7/UqdeXrb27zkBIizBvAven0/na' );
CREATE TABLE role ( id BIGINT PRIMARY KEY auto_increment, NAME VARCHAR ( 20 ) );
INSERT INTO role
VALUES
	( 1, '管理员' );
INSERT INTO role
VALUES
	( 2, '普通用户' );
CREATE TABLE role_user ( uid BIGINT, rid BIGINT );
INSERT INTO role_user
VALUES
	( 1, 1 );
INSERT INTO role_user
VALUES
	( 2, 2 );
CREATE TABLE menu ( id BIGINT PRIMARY KEY auto_increment, NAME VARCHAR ( 20 ), url VARCHAR ( 100 ), parentid BIGINT, permission VARCHAR ( 20 ) );
INSERT INTO menu
VALUES
	( 1, '系统管理', '', 0, 'menu:system' );
INSERT INTO menu
VALUES
	( 2, '用户管理', '', 0, 'menu:user' );
CREATE TABLE role_menu ( mid BIGINT, rid BIGINT );
INSERT INTO role_menu
VALUES
	( 1, 1 );
INSERT INTO role_menu
VALUES
	( 2, 1 );
INSERT INTO role_menu
VALUES
	(
	2,
	2)
3.2.3创建实体类
package com.example.demo.entity;

import lombok.Data;

@Data
public class Users {

    private Integer id;
    private String username;
    private String password;
}

3.2.4整合MyBatisPlus完成数据库操作

创建接口继承BaseMapper

package com.example.demo.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.demo.entity.Users;

public interface UsersMapper extends BaseMapper<Users> {
}

3.2.5在MyUserDetailsService调用mapper里面的方法查询数据库进行用户认证
package com.example.demo.service;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.demo.entity.Users;
import com.example.demo.mapper.UsersMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.List;

@Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {

    @Autowired
    private UsersMapper usersMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //调用usersMapper方法,更加用户名查询数据库
        QueryWrapper<Users> wrapper = new QueryWrapper<>();
        wrapper.eq("username",username);
        Users users = usersMapper.selectOne(wrapper);
        if (users == null){
            throw  new UsernameNotFoundException("用户名不再存");
        }
        List<GrantedAuthority> role = AuthorityUtils.commaSeparatedStringToAuthorityList("role");
        //从数据库中产线返回users对象,得到用户名和密码,返回
        return new User(users.getUsername(),new BCryptPasswordEncoder().encode(users.getPassword()),role);
    }
}

3.2.6在启动类上加注解

@MapperScan(“com.example.demo.mapper”)

package com.example.demo;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.example.demo.mapper")
public class DemoApplication {

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

}

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

3.3 自定义设置登录页面

3.3.1 在配置类实现相关的配置

在配置类中SecurityConfigTest,重写WebSecurityConfigurerAdapter父类方法configure(HttpSecurity http)

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

创建login.html,注意表单中name,必须是username,password,因为springSecurity中UsernamePasswordAuthenticationFilter过滤器中接受表单的名字是,

	public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
	public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form action="/user/login" method="post">
        用户名:<input type="text" name="username">
        <br/>
        用户名:<input type="text" name="password">
        <br/>
        <input type="submit" value="login">
    </form>
</body>
</html>

TestController中曾加方法用于登录成功后的跳转

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

如果是表单name名称不同可通过配置类中设置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-do8RxdJ8-1667583902396)(C:\Users\LonelyBean\AppData\Roaming\Typora\typora-user-images\image-20221020160538103.png)]

授权

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

3.4.1 hasAuthority 方法

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

  1. 在配置类中设置当前访问地址有哪些权限

    image-20221020125349647

     //当前登录用户,只有具有admins权限才可以访问这个路径
                   .antMatchers("/test/hello").hasAnyAuthority("admins")
    
  2. 在MyUserDetailsService,把返回User对象设置权限

    image-20221020125654565

 List<GrantedAuthority> role = AuthorityUtils.commaSeparatedStringToAuthorityList("admins");
3.4.2 hasAnyAuthority方法

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

image-20221020174102819

image-20221020174132080

3.4.3 hasRole方法

如果用户具备给定角色就允许访问,否则出现 403。

如果当前主体具有指定的角色,则返回 true。

源码:

    private static String hasRole(String role) {
        Assert.notNull(role, "role cannot be null");
        if (role.startsWith("ROLE_")) {
            throw new IllegalArgumentException("role should not start with 'ROLE_' since it is automatically inserted. Got '" + role + "'");
        } else {
            return "hasRole('ROLE_" + role + "')";
        }
    }

配置类中:

image-20221020175055817

3.4.4 hasAnyRole方法

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

配置类

image-20221020175536555

给用户添加角色

image-20221020175605801

3.4.5 自定义403无权限访问

image-20221020180435360

  1. 创建unauth.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h1>没有访问权限</h1>
    </body>
    </html>
    
  2. 配置类

            //配置没有权限访问跳转自定义页面
            http.exceptionHandling().accessDeniedPage("/unauth.html");
    

3.5 认证授权过程中注解使用

3.5.1@Secured

用户具有目个角色,可以访问方法

  1. 启动类或者配置类上

    @EnableGlobalMethodSecurity(securedEnabled=true)
    
  2. 增加controller方法

        @GetMapping("update")
        @Secured({"ROLE_sale","ROLE_manager"})
        public String update(){
            return "hello update";
        }
    
  3. 给用户添加角色

            List<GrantedAuthority> role = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_manager1,ROLE_sale");
    
3.5.2 @PreAuthorize

@PreAuthorize:注解适合进入方法前的权限验证,

@PreAuthorize 可以将登录用户的 roles/permissions 参数传到方法中。

  1. 启动类或配置类上开启注解

    @EnableGlobalMethodSecurity(securedEnabled=true,prePostEnabled = true)
    
  2. 添加controller注解

        @GetMapping("update")
        //@Secured({"ROLE_sale","ROLE_manager"})
        @PreAuthorize("hasAnyAuthority('admins')")
        public String update(){
            return "hello update";
        }
    
    
    
    
3.5.3@PostAuthorize
  1. @PostAuthorize 注解使用并不多,在方法执行后再进行权限验证,适合验证带有返回值的权限.

    1. 开启注解
    @EnableGlobalMethodSecurity(securedEnabled=true,prePostEnabled = true)
    
  2. 添加controller注解

   @GetMapping("/update")
    //@Secured({"ROLE_sale","ROLE_manager"})
   // @PreAuthorize("hasAnyAuthority('admins')")
    @PostAuthorize("hasAnyAuthority('admins')")
    public String update(){
        System.out.println("update.............");
        return "hello update";
    }

方法执行之后验证

image-20221020190333643

3.5.4@PostFilter

@PostFilter对方法返回的数据进行过滤

    @GetMapping("getAll")
    @PostAuthorize("hasRole('ROLE_admins')")
    @PostFilter("filterObject.username == 'admin1'")
    @ResponseBody
    public List<Users> getAllUser(){
        ArrayList<Users> list = new ArrayList<>();
        list.add(new Users(1,"admin1","6666"));
        list.add(new Users(2,"admin2","888"));
        System.out.println(list);
        return list;
    }
3.5.5@PreFilter

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

3.6用户退出操作

  1. 在配置类中添加退出的配置

            //退出
            http.logout().logoutUrl("/logout").logoutSuccessUrl("/login.html");
    
  2. 测试

    • 修改配置类,登录成功后跳转到成功页面
    • 在成功页面添加超链接,写设置退出的路径

3.7 记住我

  • cookie技术
  • 安全框架机制实现自动登录
3.7.1 实现原理

image-20221020231919138

springSecuriyt实现原理

image-20221020232613033

3.7.2 具体实现
  1. 创建数据库

    CREATE TABLE persistent_logins (
    	username VARCHAR ( 64 ) NOT NULL,
    	series VARCHAR ( 64 ) PRIMARY KEY,
    	token VARCHAR ( 64 ) NOT NULL,
    last_used TIMESTAMP NOT NULL 
    )
    
  2. 配置类中注入数据源

        @Autowired
        private DataSource dataSource;
    
        @Bean
        public PersistentTokenRepository persistentTokenRepository(){
            JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
            //自动生成数据库表
    //        jdbcTokenRepository.setCreateTableOnStartup(true);
            jdbcTokenRepository.setDataSource(dataSource);
            return new JdbcTokenRepositoryImpl();
        }
    
  3. 配置类中设置记住我

                   //记住我设置
                   .and().rememberMe().tokenRepository(persistentTokenRepository())
                   .tokenValiditySeconds(60)
                   .userDetailsService(userDetailsService)
    
  4. 在登录界面中添加复选框

记住我: <input type="checkbox" name="remember-me">自动登录

3.8 CSRF理解

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

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

​ 从 Spring Security 4.0 开始,默认情况下会启用 CSRF 保护,以防止 CSRF 攻击应用 程序,Spring Security CSRF 会针对 PATCH,POST,PUT 和 DELETE 方法进行防护。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值