SpringSecurity安全框架

本文详细介绍了Spring Security的核心概念,包括认证、会话管理、授权以及如何在实际开发中应用。通过实例展示了基于内存和数据库的用户认证,密码加密,以及权限注解的使用,最后探讨了基于数据库的认证实现和源码分析。
摘要由CSDN通过智能技术生成

        一、什么是认证和授权

                1.1、什么是认证

        进入移动互联网时代,大家每天都在刷手机,常用的软件有微信、支付宝、头条,抖音等,下边拿微信来举例子说明认证相关的基本概念,在初次使用微信前需要注册成为微信用户,然后输入账号和密码即可登录微信,输入账号和密码登录微信的过程就是认证。 系统为什么要认证? 认证是为了保护系统的隐私数据与资源,用户的身份合法,方可访问该系统的资源。 认证︰用户认证就是判断一个用户的身份是否合法的过程,用户去访问系统资源时系统要求验证用户的身份信息,身份合法 方可继续访问,不合法则拒绝访问。常见的用户身份认证方式有:用户名密码登录,二维码登录,手机短信登录,指纹认证,人脸识别等方式

                1.2、什么是会话

                用户认证通过后,为了避免用户的每次操作都进行认证可将用户的信息保证在会话中。会话就是系统为了保持当前用户的登录状态所提供的机制,常见的有基于session方式、基于token方式等。

                1.2.1、基于session的认证

                        传统项目[前端和后端代码都在一个工程下]。

        它的交互流程是,用户认证成功后,在服务端生成用户相关的数据【user对象】保存在session(当前会话setAttribute(key,user)中,发给客户端的sesssion_id存放到 cookie中,这样用户客户端请求时带上 session_id 就可以验证服务器端是否存在 session 数据,以此完成用户的合法校验,当用户退出系统或session过期销毁时,客户端的session_id 也就无效了。---有状态的方式。

                1.2.2、基于Token的认证

                        token令牌模式---JWT生成

        它的交互流程是,用户认证成功后,服务端生成一个token【唯一字符串】发给客户端,客户端可以放到 cookie 或localStorage等存储中,每次请求时带上token,服务端收到token通过验证后即可确认用户身份。---在服务端没有存储token的内容。只在客户端存储

基于session的认证方式由servlet规范定制,服务端要存储session信息需要占用内存资源,客户端需要支持cookie;基于token的方式则一般不需要服务端存储token,并且不限制客户端的存储方式。如今移动互联网时代更多类型的客户端[pC,android,IOS,]需要接入系统,系统多是采用前后端分离的架构进行实现,所以基于token的方式更适合。

                1.3、什么是授权

                还拿微信来举例子,微信登录成功后用户即可使用微信的功能,比如,发红包、发朋友圈、添加好友等,没有绑定银行卡的用户是无法发送红包的,绑定银行卡的用户才可以发红包,发红包功能、发朋友圈功能都是微信的资源即功能资源,用户拥有发红包功能的权限才可以正常使用发送红包功能,拥有发朋友圈功能的权限才可以便用发朋友圈功能,这个根据用户的权限来控制用户使用资源的过程就是授权

                1.3.1、为什么要授权

                认证是为了保证用户身份的合法性,授权则是为了更细粒度的对隐私数据进行划分,授权是在认证通过后发生的,控制不同的用户能够访问不同的资源。 授权:授权是用户认证通过根据用户的权限来控制用户访问资源的过程,拥有资源的访问权限则正常访问,没有权限则拒绝访问。

                

                在实际开发中:认证和授权设计到的表结构:

                用户---登录后---角色----权限----操作哪些接口资源

         我们在一期项目时,也可以人为的完成认证和授权功能。但是比较麻烦。而且我们考虑的因素肯定没有别人多,所以我们可以使用别人的安全框架 来完成认证和授权功能。而这里常用的安全框架有: shiro 和 springsecurity. shiro这个框架之前班级讲解。 springsecurity[这个多。因为spring全家桶] 。

                1.4、springsecurity

                

Spring Security 是一个功能强大且高度可定制的身份验证和访问控制框架。它是保护基于 Spring 的应用程序的事实标准。

Spring Security 是一个专注于为 Java 应用程序提供身份验证和授权的框架。与所有 Spring 项目一样,Spring Security 的真正强大之处在于它可以轻松扩展以满足自定义需求.

Springsecurity它就是一个身份认证和授权的框架。----单单的使用是非常简单的---但是如果你想往深的理解它,比较复杂。

                二、快速使用Springsecurity

                整合springboot

         (1)定义一个资源类               

@RestController
public class HelloController {


    @GetMapping("/hello")
    public String hello(){
        System.out.println("hello===================");
        return "快速入门springsecurity";
    }
}

        (2)启动服务并访问该资源

 

        它跳转到登录界面,原因:你使用了springsecurity启动依赖,它就会加载很多过滤器。当你访问某个资源时,没有登录。则UsernamePasswordAuthenticationFilter 跳转到登录界面。而且该界面是springsecurity提供了。 默认的账户为user 密码在控制台

                三、自定义账户和密码

                通过上面的代码,我们知道springsecurity帮你生成了一个默认的账户和密码。我们可以自定义账户和密码。 再到后面我们会使用数据库中的账户和密码。

                (1)创建一个配置类并集成WebSecurity

//注意:如果你的springboot版本过高,则该WebSecurityConfigurerAdapter过时。
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    //构建认证的账户和权限
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //基于内存构建自定义账户和密码----【后面会基于数据库】
        auth.inMemoryAuthentication()
                //账户
                .withUser("admin")
                //密码
                .password("123456")
                //该账户具有的角色
                .roles("admin")
                .and()
                .withUser("ykq")
                .password("123456")
                .roles("test");
    }
}

         

                输入你构建的 账户和密码发现无法登录, 原因: springsecurity的密码要求必须加密。你输入的密码和springsecurity加过密的密码是不一致的。 解决办法: 就是你输入的密码也要使用密码加密器来进行加密,这样才能密码匹配。

                四、密码加密器

public class Test {
    public static void main(String[] args) {
        //PasswordEncoder:它是密码加密器的父容器。它下面有很多实现类。
          //第一个方法: encode:加密。
         //第二个方法: matches:匹配。
        PasswordEncoder passwordEncoder=new BCryptPasswordEncoder();

        String encode1 = passwordEncoder.encode("123456");
        String encode2 = passwordEncoder.encode("123456");
        String encode3 = passwordEncoder.encode("123456");

        /*
        $2a$10$qvFsAAqdKwaofY/HpcyxVOicxEJ6HW9gbCUwxSsnQkil0dUAhurhS
        $2a$10$t8uN186zqYb9scKEHraUiemINCKi3nusyz1TutHCtEjZJ5haIeRSG
        $2a$10$SdE6bG9meaa/GWcokOIXBOrHA08PuCtGDjYhwNvPV4cpABQP/Dlk.
        发现:同一个原文经过加密后,密文不同。每次加密得到的密文不一样。只要他们使用的是同一个加密器,那么就可以判断是否相同。
        安全行比较高。
         */
        System.out.println(encode1);
        System.out.println(encode2);
        System.out.println(encode3);

        boolean matches1 = passwordEncoder.matches("123456", "$2a$10$qvFsAAqdKwaofY/HpcyxVOicxEJ6HW9gbCUwxSsnQkil0dUAhurhS");
        boolean matches2 = passwordEncoder.matches("123456", "$2a$10$t8uN186zqYb9scKEHraUiemINCKi3nusyz1TutHCtEjZJ5haIeRSG");
        boolean matches3 = passwordEncoder.matches("123456", "$2a$10$SdE6bG9meaa/GWcokOIXBOrHA08PuCtGDjYhwNvPV4cpABQP/Dlk.");

        System.out.println(matches1);
        System.out.println(matches2);
        System.out.println(matches3);


    }
}

        五、获取当前用户信息

                当前用户--登录的账户信息。security认证通过后,会把当前用户具有的信息SecurityContext类中。你可以把它当作session对象。

@RestController
@RequestMapping("user")
public class UserController {


    @GetMapping("info")
    public Authentication info(){
        //获取security上下文对象--
        SecurityContext securityContext = SecurityContextHolder.getContext();
        //用户的认证信息都封装到Authentication类中
        Authentication authentication = securityContext.getAuthentication();

        return authentication;
    }

}

 

        六、授权的功能

                如果当前用户具有某个权限,方可访问对应的资源。授权是发生再认证后。

                admin: user:insert user:query user:delete user:update

                ykq: user:query user:export        

    //构建认证的账户和权限.
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //基于内存构建自定义账户和密码----【后面会基于数据库】
        auth.inMemoryAuthentication()
                //账户
                .withUser("admin")
                //密码--数据库
//                .password("$2a$10$qvFsAAqdKwaofY/HpcyxVOicxEJ6HW9gbCUwxSsnQkil0dUAhurhS")
                .password(passwordEncoder.encode("123456"))
                //该账户具有的角色
                .roles("admin")
                //该账户分配权限
                .authorities("user:delete","user:insert","user:update","user:query")
                .and()
                .withUser("ykq")
               // .password("$2a$10$SdE6bG9meaa/GWcokOIXBOrHA08PuCtGDjYhwNvPV4cpABQP/Dlk.")
                .password(passwordEncoder.encode("123456"))
                .roles("test")
                .authorities("user:query","user:export");


    }

                我们上面的代码模拟了两个账户,并为这两个账户分配了相应的权限。 这些权限可以操作哪些资源。需要人为的绑定。 

    @GetMapping("delete")
    public String delete(){

        return "用户删除的操作";
    }
   
    @GetMapping("insert")
    public String insert(){

        return "用户添加的操作";
    }
    @GetMapping("update")
    public String update(){

        return "用户修改的操作";
    }
    @GetMapping("query")
    public String query(){

        return "用户查询的操作";
    }
    @GetMapping("export")
    public String export(){

        return "用户导出的操作";
    }

                修改security类

 @Override
    protected void configure(HttpSecurity http) throws Exception {

        //放行登录表单资源
        http.formLogin().permitAll();

        //资源绑定
        http.authorizeRequests()
                //访问哪些资源时,需要具有的权限。
                .antMatchers("/user/delete").hasAuthority("user:delete")
                .antMatchers("/user/insert").hasAuthority("user:insert")
                .antMatchers("/user/query").hasAuthority("user:query")
                .antMatchers("/user/update").hasAuthority("user:update")
                .antMatchers("/user/export").hasAuthority("user:export");

        //其他请求必须认证后才能访问
        http.authorizeRequests().anyRequest().authenti
            cated();

    }

        七、使用注解完成权限的绑定

                上面我们是通过代码的形式,对权限和资源进行了绑定关系。这样会非常麻烦。 我们security就提高了注解的方式。

                

                修改配置类

                开启security的注解

                再相应的资源方法上使用权限注解

@GetMapping("delete")
    @PreAuthorize(value = "hasAuthority('user:delete')")
    public String delete(){
        return "用户删除的操作";
    }
    @GetMapping("insert")
    @PreAuthorize(value = "hasAuthority('user:insert')")
    public String insert(){

        return "用户添加的操作";
    }
    @GetMapping("update")
    @PreAuthorize(value = "hasAuthority('user:update')")
    public String update(){

        return "用户修改的操作";
    }
    @GetMapping("query")
    @PreAuthorize(value = "hasAuthority('user:query')")
    public String query(){

        return "用户查询的操作";
    }
    @GetMapping("export")
    @PreAuthorize(value = "hasAuthority('user:export')")
    public String export(){

        return "用户导出的操作";
    }

        八、零散的知识

@Override
    protected void configure(HttpSecurity http) throws Exception {

        //放行登录表单资源
        http.formLogin()
                .loginProcessingUrl("/login")
                //登录成功后跳转的路径。默认跳转之前的路径。 success提交方式必须为post提交
                .successForwardUrl("/success")
                .permitAll();

        //设置权限不足时,跳转的路径
        http.exceptionHandling().accessDeniedPage("/fail");
        //资源绑定
//        http.authorizeRequests()
//                //访问哪些资源时,需要具有的权限。
//                .antMatchers("/user/delete").hasAuthority("user:delete")
//                .antMatchers("/user/insert").hasAuthority("user:insert")
//                .antMatchers("/user/query").hasAuthority("user:query")
//                .antMatchers("/user/update").hasAuthority("user:update")
//                .antMatchers("/user/export").hasAuthority("user:export");

        //其他请求必须认证后才能访问
        http.authorizeRequests().anyRequest().authenticated();

    }

        九、查看security认证的源码流程

                UsernamePasswordAuthoricationFilter账户密码认证过滤器。

这里的UserDetailsService是如果想使用自定义,则可以自建创建一个类并实现UserDeatailService接口,并把该类交于容器来管理。 

        十、基于数据库完成认证

                10.1、创建一个类并实现UserdetailService接口

@Service
public class MyUserDetailService implements UserDetailsService {
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        System.out.println("执行了自己根据账户查询用户信息====================");
        return null;
    }
}

                10.2、修改security配置类

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值