SpringSecurity入门(超级无敌认真好用,万字收藏篇!!!!)

SpringSecurity入门

本文属于SpringSecurity入门篇,后续学习过程中会持续更新

基于spring的安全框架

前言

什么是安全框架?

解决系统安全问题的框架,如果没有安全框架,我们就需要手动处理每个资源的访问控制,显得非常麻烦。使用安全框架后就可以使用配置的方式对资源进行访问控制。

常见的安全框架

  • Apache Shiro:一个功能强大且易于使用的安全框架,提供了认证,授权,加密,会话管理。
  • Spring Security:Spring家族中的一员,是一个能够为应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组Spring上下文配置的Bean,利用SpringIoc,DI和AOP功能,为应用系统提供声明式的安全访问控制功能,减少了企业系统安全控制编写大量重复代码工作。

1 SpringSecurity概述

SpringSecurity是一个高度自定制的安全框架,他利用SpringIoc,DI,AOP功能,为应用系统提供声明式的安全访问控制功能,减少企业系统安全控制编写大量重复代码工作。

SpringSecurity两大核心功能

  1. 认证:是建立一个主体的过程("主体"一般指用户,设备或可以在应用程序中执行动作的其他系统),简单说是指使用者通过账户名和密码登陆的整个过程称为认证。
  2. 授权:是指一个主体是否允许在应用程序中执行某个动作的过程,简单说就是给某个用户指定某个功能的访问权限。
  • SpringSecurity通过过滤器实现请求拦截,实现认证,授权等操作。

2 SpringSecurity的基本使用

  1. 引入Springboot和SpringSecurity相关依赖

       <parent>
            <groupId>org.springframework.boot</groupId>
            <version>2.7.9</version>
            <artifactId>spring-boot-starter-parent</artifactId>
        </parent>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-security</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <excludes>
                            <exclude>
                                <groupId>org.projectlombok</groupId>
                                <artifactId>lombok</artifactId>
                            </exclude>
                        </excludes>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
  2. 创建springboot启动类

    @SpringBootApplication
    public class Application {
        public static void main(String[] args) {
            SpringApplication.run(Application.class);
        }
    }
    
  3. 创建Controller

    @RestController
    @RequestMapping("/test")
    public class TestController {
    
        @RequestMapping("/test01")
        public String test01(){
            return "test01";
        }
    
        @RequestMapping("/test02")
        public String test02(){
            return "test02";
        }
    }
    
  4. 启动测试跳入如下页面

    当在项目中引入SpringSecurity依赖后,整个项目就会被SpringSecurity管理起来

    未认证用户无权进入系统,会自动跳转到SpringSecurity提供的登陆页面

    在这里插入图片描述

  • 如图我们需要登陆才能访问

    密码在每次服务器启动时,控制台会自动生成一串字符串,如图所示

    默认用户名:user

    在这里插入图片描述

  1. 输入用户名和密码,进入test01页面

    在这里插入图片描述

  2. 自定义SpringSecurity登陆页面

    如果要使用自定义页面,需要SpringSecurity配置类中指定

    • 继承WebSecurityConfigurerAdapter

      • public class SecurityConfig extends WebSecurityConfigurerAdapter

        继承的方式可能会存在安全隐患,此方式已过时

    • 组装式定义SpringSecurity配置类

      /**
       * SpringSecurity配置类
       */
      @Configuration
      public class SecurityConfig {
      }
      
  • SecurityFilterChain方法

    用于拦截请求,并对请求进行处理

        /**
         * 用于拦截请求,并对请求进行处理
         * @param httpSecurity
         * @return  SecurityFilterChain:Security过滤器链
         */
        @Bean
        @Autowired
        public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
         httpSecurity
                 .csrf().disable()//禁用跨域请求伪造的攻击
                 //请求配置
                 .authorizeRequests()//获得所有认证请求
                     //防止发生重定向次数过多错误,需要将login.html放行
                    .antMatchers("/login.html","/fail.html")//匹配指定的路径
                        .permitAll()//无需认证即可访问
                    .anyRequest()//获得任意请求
                        .authenticated()//必须认证才允许访问
                 .and()
                 //配置form表单
                 .formLogin()//配置表单
                 	.loginProcessingUrl("/login")//配置登陆处理器的url地址,该地址所对应处理类由SpringSecurity提供
                    .loginPage("/login.html")//配置登陆页
                    .failureUrl("/fail.html")//登陆失败处理页
            ;
         return httpSecurity.build();//获得securityFilterChain的实现类对象并返回
        }
    
  • 上述拦截到页面直接无需认证让其放行,还有一种方式

    若拦截页面很多,则使用下列方式

        /**
         * 处理需要忽略的请求
         * @return
         */
        @Bean
        public WebSecurityCustomizer webSecurityCustomizer(){
            return new WebSecurityCustomizer(){
                @Override
                public void customize(WebSecurity web) {
                    //配置不拦截的路径(要忽略的路径)
                       web.ignoring().antMatchers("/login.html","/fail.html");
                }
            };
        }
    

3 SpringSecurity基于内置账户的实现

  • 设置账户时,每个账户都必须有一个角色
  • 密码必须加密,通过BCryptPasswordEncoder进行加密

SpringSecurity把设置的内存密码交给加密器进行加密,获得一个"盐",通过"盐"和输入的密码进行加密获得一个加密后的字符串,和设置的内存密码进行匹配,若一样登陆成功

用于处理认证和授权逻辑

  • 该方法内,可以定义认证和授权相关操
  • 此处功能:
    •  设置内存账户,根据内存账户自定义用户名和密码进行登陆
      
    •  `@param builder`    认证管理器的编译器对象,该对象由springSecurity自动注入
      
    /**
     *用于处理认证和授权逻辑
     * 该方法内,可以定义认证和授权相关操作
     * 此处功能:
     *      -设置内存账户,根据内存账户自定义用户名和密码进行登陆
     * @param builder    认证管理器的编译器对象,该对象由springSecurity自动注入
     */
    @Autowired
    public void registerProvider(AuthenticationManagerBuilder builder) throws Exception {
        //SpringSecurity要求密码必须加密,创建加密器对象
        PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        builder
                        .inMemoryAuthentication()//设置内存账户
                            .passwordEncoder(passwordEncoder)//设置密码加密器对象
                            .withUser("admin")//设置内置账户用户名
                            .password(passwordEncoder.encode("123456"))//设置内置账户密码
                            //SpringSecurity要求每个账户必须有一个角色,此处定义账户为设置角色
                            .roles("USER")//设置角色
                        ;
    }
  • 加入角色后,每个资源都必须设置一个角色

    在SecurityFilterChain方法设置

                    .antMatchers("/index.html")
                        .hasAnyRole("USER","ADMIN")
                    .antMatchers("/test/test01")
                        .hasRole("ADMIN")
                    .antMatchers("/test/test02")
                        .hasAnyRole("USER")
    
    • 上面设置允许角色"USER","ADMIN"访问资源index.html

      允许角色"USER"访问资源test/test01

      允许角色"ADMIN"访问资源test/test02

4 SpringSecurity基于数据库的实现

  • 创建bean

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class UserInfo implements Serializable {
        private Integer user_id;
        private String user_name;
        private String user_password;
        private String user_email;
        private Date user_birthday;
        private String user_hobbys;
        private Integer user_sex;
        private String user_address;
        private Integer user_status;
    
    }
    
  • 声明UserDetailsService对象并注入进认证管理器

    @Autowired
        private UserDetailsService userDetailsService; 
    @Autowired
        public void registerProvider(AuthenticationManagerBuilder builder) throws Exception {
            //SpringSecurity要求密码必须加密,创建加密器对象
            PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
            /**
             * 基于数据库
             */
            builder.userDetailsService(userDetailsService)//用于指定登陆的处理逻辑(认证逻辑)的对象
                    .passwordEncoder(passwordEncoder)//设置密码加密器
            ;
        }
    
  • 创建UserDetailsServiceImpl类并实现UserDetailsService接口

    该类需要实现接口中的loadUserByUsername方法,该方法返回值为UserDetails

    该方法作用

    1. 根据用户名获得用户信息
    2. 将用户信息认证需要的数据封装到UserDetails的实现类对象中(User)
    3. 将封装好的认证信息提交给SpringSecurity进行认证

    UserDetails接口中方法:

    • Collection<? extends GrantedAuthority> getAuthorities();获得权限集合
    • String getPassword();获得账号
    • String getUsername();获得密码
    • boolean isAccountNonExpired();判断账户是否过期(有效)
    • boolean isAccountNonLocked();判断账户是否被冻结
    • boolean isCredentialsNonExpired();凭证是否过期
    • boolean isEnabled();账户是否启用
    /**
     * 处理认证逻辑的类
     * + 该类需要重写接口中的loadUserByUsername方法根据用户名获得用户对象
     */
    @Service
    public class UserDetailsServiceImpl implements UserDetailsService {
        /**
         * 1.根据用户名获得用户信息
         * 2.将用户信息认证需要的数据封装到UserDetails的实现类对象中(User)
         * 3.将封装好的认证信息提交给SpringSecurity进行认证
         * @param username
         * @return
         * @throws UsernameNotFoundException
         */
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            return null;
        }
    }
    
  • 由于要根据用户名获得用户信息则创建UserMapper接口

    @Repository
    public interface UserMapper {
    
        /**
         *
         * 根据用户名获得用户对象
         * @param username
         * @return
         */
        @Select("select * from tbl_user where user_name=#{username}")
        public UserInfo getUserByUsername(String username);
    }
    
  • 根据三步完善UserDetailsServiceImpl

     	@Resource
        private UserMapper userMapper;
        /**
         * 1.根据用户名获得用户信息
         * 2.将用户信息认证需要的数据封装到UserDetails的实现类对象中(User)
         * 3.将封装好的认证信息提交给SpringSecurity进行认证
         * @param username
         * @return
         * @throws UsernameNotFoundException
         */
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            //根据用户名获得用户信息
            UserInfo userInfo = userMapper.getUserByUsername(username);
            //检测用户名是否正确
            if (userInfo==null){
                  log.info("用户名不存在");
                  return null;
            }
            //检测用户是否为有效用户
            if (userInfo.getUser_status()==-1){
                log.info("用户被冻结");
                return null;
            }
            List<GrantedAuthority>  authorities =new ArrayList<>();
            authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
            //将UserInfo中认证需要数据封装到User对象中(UserDetails的实现类对象)
            User user = new User(userInfo.getUser_name()//附加数据
                             ,userInfo.getUser_password()//密码
                             ,true//账户是否启用
                             ,true//账户是否过期
                             ,true//凭证是否过期
                             ,userInfo.getUser_status()==-1?false:true//账户是否被锁定
                             ,authorities);//账户拥有的权限
            return user;//将该返回值交给SpringSecurity进行认证
    
        }
    

ps:小知识点

  • 前端的文本框和密码框name只能是username和password,要想使用其他name该如何解决

在配置from表单里设置

.formLogin()//配置表单
                    .loginProcessingUrl("/login")//配置登陆处理器的url地址,该地址所对应处理类由SpringSecurity提供
                    .loginPage("/login.html")//配置登陆页
                    .failureUrl("/fail.html")//登陆失败处理页
               		.usernameParameter("myusername")//前端文本框的name
           	        .passwordParameter("mypassword")//前端密码框的name

  • 学习来自于西安加中实训

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

@活着笑

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

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

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

打赏作者

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

抵扣说明:

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

余额充值