SpringSecurity学习笔记

Spring Security

项目搭建使用流程

1. 搭建项目
  • 创建好springboot框架

  • 在pom文件导入springsecurity框架的依赖包

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    
  • 编写一个controller类,测试方法

    @RestController
    @RequestMapping("/test")
    public class DemoController {
    
        @GetMapping("/demo")
        public String demo() {
            return "欢迎";
        }
    }
    
  • 启动项目,浏览器访问当前项目地址:http://localhost:8080/test/demo

在这里插入图片描述

访问网址后会跳转到一个默认的登录页面

账号:user 密码会在控制台中随机生成出来
在这里插入图片描述

2. 设置登录的用户名和密码
  1. 第一种方法:通过配置文件

    server:
      port: 9999
    spring:
      security:
        user:
          name: admin
          password: admin
    
  2. 第二种方式:通过配置类

    @Configuration
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
            /*
                将账号和加密后的密码放入seciruty的容器中
                passwordEncoder:指定加密方式(MD5,BCrypt )
                withUser:账号
                password:加密后的密码
                roles:权限(角色)
             */
            authenticationManagerBuilder.inMemoryAuthentication()
                    .passwordEncoder(new BCryptPasswordEncoder())
                    .withUser("admin")
                    .password(new BCryptPasswordEncoder().encode("123"))
                    .roles("admin");
        }
    }\
    
  3. 第三种方式:自定义编写实现类

    • 创建配置类,设置使用哪个userDetailsService实现类

      @Configuration
      public class SecurityTestConfig extends WebSecurityConfigurerAdapter {
          @Autowired
          private UserDetailsService userDetailsService;
      
          @Override
          protected void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
      
              authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
          }
      }
      
    • 编写实现类,返回User对象,User对象有用户名密码和操作权限

      @Service("userDetailsService") //注入名称必须是userDetailsService
      public class MyUserDetailsService implements UserDetailsService {
      
          //在下面的方法执行查询数据库,判断用户密码的操作
          @Override
          public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
              List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("role");
              return new User("admin", new BCryptPasswordEncoder().encode("123"), authorities);
          }
      }
      
3. 查询数据库完成用户认证
  1. 导入mybatis-plus和mysql依赖包
        <!--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>	
  1. 创建数据库和表结构
DROP TABLE IF EXISTS `users`;
CREATE TABLE `users`  (
  `id` int(11) NOT NULL COMMENT 'id主键',
  `user_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户名',
  `pass_word` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
  1. 创建对应的实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Users {
    private Integer id;
    private String userName;
    private String passWord;
}
  1. 整合mybatis-plus,创建接口(mapper),继承mybatis-plus的接口
@Mapper
public interface UserMapper extends BaseMapper<Users> {
}
  1. 在MyUserDetailsService类调用mapper里面的方法查询数据库进行用户认证
@Service("userDetailsService") //注入名称必须是userDetailsService
public class MyUserDetailsService implements UserDetailsService {

    @Autowired
    private UserMapper userMapper;

    //在下面的方法执行查询数据库,判断用户密码的操作
    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
        //调用userMapper方法查询数据库
        QueryWrapper<Users> wrapper = new QueryWrapper<>();
        wrapper.eq("user_name", userName);
        Users users = userMapper.selectOne(wrapper);

        //判断数据库是否有查询的用户名
        if (users == null) {
            throw new UsernameNotFoundException("用户名不存在!");
        }

        //权限,不能为空
        List<GrantedAuthority> authorities =
                AuthorityUtils.commaSeparatedStringToAuthorityList("role");

        //得到数据库查询的账号密码,返回
        return new User(users.getUserName(),
                new BCryptPasswordEncoder().encode(users.getPassWord()),
                authorities);
    }
}
  1. 在启动类添加注解@MapperScan扫描mapper文件夹
@MapperScan("springsecurt.mapper")
@SpringBootApplication
public class SpringsecurtApplication {

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

}
  1. 配置数据库信息
spring:
  datasource:
    username: root
    password: root
    url: jdbc:mysql://localhost:3306/demo?useSSL=false&serverTimezone=GMT%2B8
    driver-class-name: com.mysql.cj.jdbc.Driver
4. 自定义配置信息
  1. 在配置类(SecurityTestConfig类)实现相关配置

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

    login.html页面

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <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="登录">
    </form>
    </body>
    </html>
    

    (DemoController类)test/hello 请求方法

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

    访问http://localhost:9999/test/hello,正常访问,返回数据,这是前面已经把这个请求地址的权限公开了

在这里插入图片描述

访问一个普通的地址http://localhost:9999/test/demo,会拦截并跳转到自己配置的登录页面
在这里插入图片描述

输入数据库正确的账号密码后,正常跳转到要访问的地址

在这里插入图片描述

5. 角色或权限控制

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

权限不匹配或无权限访问地址,页面会返回403状态

hasAuthority和hashAnyAuthority的区别:

  • hasAuthority只支持一个权限角色(例:管理员)
  • hashAnyAuthority则支持多个权限角色(例:管理员,客户,普通用户)
设置了公开请求地址,并同时设置了权限控制必须要登录才行
  1. 第一种方法:hasAuthority

    1. 在配置类设置当前访问地址有哪些权限(在SecurityTestConfig类的configure方法里)

      //当前登录用户,只有具有admins权限才可以访问这个路径
      .antMatchers("/test/hello").hasAuthority("admins")
      
    2. 在UserDetailService,把返回User对象设置权限(访问地址时需要的权限字符一致)

]

  1. 第二种方法:hasAnyAuthority

    支持多权限/角色 访问

    //当前登录用户,拥有admins和ordinary任意一个权限都可以访问
    .antMatchers("/test/hello").hasAnyAuthority("admins","ordinary")
    
  2. 第三种方法:haseRole

    如果用户具备指定的角色,则返回true,对应的权限字符前缀必须是 ROLE_

    .antMatchers("/test/hello").hasRole("sale")
    
    List<GrantedAuthority> authorities =
            AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_sale");
    
  3. 第四种方法:hasAnyRole

    和第二种一样,支持多角色/资源访问,对应的权限字符前缀必须是 ROLE_

6. 自定义无权限页面(403)
  • 创建页面

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h1>当前是403页面</h1>
    </body>
    </html>
    
  • 配置信息

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

在这里插入图片描述

7. 注解使用

@Securied

用户具有某个角色,可以访问方法,该注解支持多个角色权限

  • 启动类开启注解

    @EnableGlobalMethodSecurity(securedEnabled = true)
    
  • 在controller方法上面设置,设置角色

    @RequestMapping("/update")
    @Secured({"ROLE_sale", "ROLE_manager"})
    public String update() {
        return "hello update";
    }
    
  • 在MyUserDetailsService设置角色

    //权限,不能为空
    List<GrantedAuthority> authorities =
            AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_sale");
    

@PreAuthorize

在方法上面加上注解,指定需要访问的角色

@PreAuthorize("hasAnyAuthority('sale')")

在MyUserDetailsService设置对应的角色

//权限,不能为空
List<GrantedAuthority> authorities =
        AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_sale");

@postAuthorize

先执行完当前方法,再进行判断是否有当前方法的权限

@PostAuthorize("hasAnyAuthority('admins')")

@PostFillter

方法返回数据进行过滤

  • PostFilter的参数指定了 users对象里面的 userName的值必须是**‘admins1’**
  • 返回的数据只返回带有admins1的数据
    @RequestMapping("/update")
    @PostAuthorize("hasAnyAuthority('admins')")
    @PostFilter("filterObject.userName=='admin1'")
    public Object update() {
        List<Users> list = new ArrayList<>();
        list.add(new Users(11,"admin1","123"));
        list.add(new Users(22,"admin2","123"));
        return list;
    }

@PreFilter

对请求参数进行过滤

PreFilter的参数指定了id大于10才可以请求,否则就无权限

@RequestMapping("/update")
@PostAuthorize("hasAnyAuthority('admins')")
@PreFilter("filterObject.id<10")
public Object update(User user) {
    return hello;
}
8. 用户注销
  1. 在配置类添加退出的配置

    //退出登录请求路径
    httpSecurity.logout().logoutUrl("/logout")
            .logoutSuccessUrl("/test/hello").permitAll();
    
  2. 在页面写上退出的请求地址

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    登录成功
    <a href="/logout">退出</a>
    
    </body>
    </html>
    
  3. 正常登陆后,进入这个页面,然后点击退出按钮,再去访问其他接口发现需要重新登录,测试完成

9. 自动登陆

思路:登录 -->认证成功 -->surice将tokend写入数据库,并返回浏览器,写入cookie -->下次请求 -->读取浏览器cookie中的token发送到surice -->在数据库查找token,有页面正常访问,无需要登录

实现过程:

  1. 创建数据库表

    表名必须是persistent_logins,字段名也是一个都不能改,springsecurity默认的方法只认这些

    DROP TABLE IF EXISTS `persistent_logins`;
    CREATE TABLE `persistent_logins`  (
      `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '用户名',
      `series` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '系列',
      `token` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '令牌',
      `last_used` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '最后的时间'
    ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
    
  2. 配置类,注入数据源,配置操作数据库对象

    //注入数据源
    @Autowired
    private DataSource dataSource;
    
    @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
        jdbcTokenRepository.setDataSource(dataSource);
        return jdbcTokenRepository;
    }
    
  3. 配置类配置自动登陆

    .and().rememberMe().tokenRepository(persistentTokenRepository())
    .tokenValiditySeconds(60) //设置有效时长
    .userDetailsService(userDetailsService)
    
  4. 在登录页面添加复选框

    name名字必须是remember-me

    <input type="checkbox" name="remember-me"/>自动登录
    

    在页面登陆后,数据库就会存入信息

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

8. csrf,跨站请求伪造

注释掉配置的关闭crsf的防护

//                .and().csrf().disable(); //关闭csrf防护

在前端请求加一串代码

在这里插入图片描述

补充

  • 本技术基于SpringBoot技术完成使用

  • 核心功能:认证,授权

  • 重量级框架,需要依赖其他组件,需要配置很多信息

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大叔叔哦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值