美文网首页
高级框架第七天Spring Security:安全管理框架

高级框架第七天Spring Security:安全管理框架

作者: w漫漫 | 来源:发表于2020-08-31 15:58 被阅读0次

    一.Spring Security简介

    1.概括

    Spring Security是一个高度自定义的安全框架.利用Spring IoC/DI和AOP功能,为系统提供了声明式安全访问控制功能,减少了为系统安全而编写大量重复代码的工作

    使用Spring Security的原因有很多,单大部分都是发现了javaEE的Servlet规范或EJB规范中的安全工鞥呢缺乏典型企业应用场景.同时认识到他们在WAR或EAR级别无法移植.因此如果你更换服务器环境,还有大量工作去重新配置你的应用程序.使用Spring Security解决了这些问题,也为你提供许多其他有用的,可定制的安全功能

    正如你可能知道的关于安全方面的两个主要区域是"认证"和"授权"(或者访问控制).这两点也是Spring Security重要核心功能."认证",是建立一个他声明的主体的过程(一个"主体"一般是指用户,设备或一些可以在你的应用程序中执行动作的其他系统),通俗点说就是系统认为用户是否能登录."授权"指确定一个主体是否允许在你的应用程序执行一个动作的过程.通俗点讲就是系统判断用户是否有权限去做某些事情

    2.历史

    Spring Security以"The Acegi Security System for Spring"的名字始于2003年年底.器前身为acegi项目.起因是 Spring开发邮件列表中一个问题,有人提问是否考虑提供一个基于Spring的安全实现.限制于时间问题,开发出了一个简单的安全实现,但是并没有深入研究.几周后,Spring社区中其他成员同样询问了安全问题,代码提供给了这些人.2004年1月份已经有20人左右使用这个项目.随着更多人的加入,在2004年3月左右在sourceforge中建立了一个项目.在最开始并没有认证模块,所有的认证功能都是依赖容器完成的,而acegi则注重授权.但是随着更多人的使用,基于容器的认证就显现出了不足.acegi中也加入了认证功能.大约1年后acegi成为Spring子项目

    在2006年5月发不了acegi1.0.0版本.2007年底acegi更名为Spring Security

    二.第一个Spring Security项目

    1.导入依赖

    Spring Security已经被Spring boot进行集成,使用时直接引入启动器即可.

    <dependency>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-security</artifactId>

    </dependency>

    2.访问页面

    导入spring-boot-starter-security启动器后,Spring Security已经生效,默认拦截全部请求,如果用户没有登录,跳转到内置登录页面

    在项目中新建login.html页面后

    在浏览器输入:http://localhost:8080/login.html后会显示下面页面

    默认的username为user,password打印在控制台中.

    在浏览器中输入账号和密码后会显示login.html页面内容

    3.应用场景

    3.1对已有项目添加认证功能

    在很多技术中心都可能有web访问控制页面.例如:solr就有web管理页面.不需要进行登录,只要知道ip和端口任何人都可以进行访问的.可能导致solr中数据不安全问题.为了保证数据安全性,可以想办法添加Spring Security.(实际上无法添加的,非maven项目)后面还会学很多其他技术,也可以按照这个思想进行操作

    3.2对常规项目

    需要修改application.properties(application.yml)

    spring.security.user.name=root

    spring.security.user.password=root

    三.UserDetailsService详解

    当什么也没有配置的时候,账号和密码是由Spring Security定义生成的.而在实际项目中账号和密码都是从数据库中查询出来的.所以我们要通过自定义逻辑控制认证逻辑

    如果需要自定义逻辑时,只需要实现UserDetailsService接口即可.接口定义如下:

    1.返回值UserDetails是一个接口,定义如下

    要想返回UserDetails的实例就只能返回接口的实现类.Spring Security中提供了如下的实例.对于我们只需要使用里面的User类即可.注意User的全限定路径是:

    org.springframework.security.core.userdetails.User

    此处京城和系统中自己开发的User类弄混

    在User类中提供了很多方法和属性

    其中构造方法有两个,调用其中任何一个都可以实例化UserDetails实现类User类的实例.而三个参数的构造方法实际上也是调用7个参数的构造方法

    username:用户名

    password:密码

    authorities:用户具有的权限.此处不允许为null

    此处的用户名应该是客户端传递过来的用户名.而面膜应该是从数据库中查询出来的面膜.Spring Security会根据User中的password和客户端传递过来的password进行比较.如果系统则表示认证通过,如果不相同表示认证失败

    authorities里面的权限对于后面学习授权是很有必要的,包含的所有内容为此用户具有的权限,如果里面没有包含某个权限,而在做某个事情时必须包含某个权限则会出现403.通常都是通过AuthorityUtils.commaSeparatedStringToAuthorityList("")来创建authorities集合对象.参数是一个字符串,多个权限使用逗号分隔

    2.方法参数

    方法参数表示用户名.此值是客户端表单传递过来的数据.默认情况下必须叫username,否则无法接收

    3.异常

    UsernameNotFoundException用户名没有发现异常.在loadUserByUsername中是需要通过自己的逻辑从数据库中取值的.如果通过用户名没有查询到对应的数据,应该抛出UusernameNotFoundException,系统就知道用户名没有查询到

    四.PasswordEncoder密码解析器详解

    Spring Security要求容器中必须有PasswordEncoder实例(客户端面膜和数据库密码是否匹配是由Spring Security去完成的,Security中还没有默认密码解析器).所以当自定义登录逻辑时要求必须给容器注入PasswordEncoder的bean对象

    1.接口介绍

    encode():把参数按照特定的解析规则进行解析

    matches()验证从存储中获取的编码密码与编码后提交的原始面膜是否匹配.如果面膜匹配,则返回true;如果不匹配,则返回false.第一个参数表示需要被解析的面膜.第二个参数表示存储的密码

    upgradeEncoding()如果解析的面膜能够再次进行解析且达到更安全的结果则返回true,否则返回false.默认返回false

    2.内置解析器介绍

    在Spring Security中内置了很多解析器

    3.BCryptPasswordEncoder简介

    BCryptPasswordEncoder是Spring Security官方推荐的面膜解析器,平时多使用这个解析器

    BCryptPasswordEncoder是对bcrypt强散列方法的具体实现.是基于Hash算法实现的单向加密.可以通过strength控制加密强度,默认10

    4.代码演示

    在项目src/test/java下新建com.bjsxt.MyTest测试BCryptPasswordEncoder用法

    @SpringBootTest

    @RunWith(SpringRunner.class)

    public class MyTest{

        @Test

        public void test(){

            //创建解析器

            PasswordEncoder encoder = new BCryptPasswordEncoder();

            //对密码进行加密

            String password = encoder.encode("123");

             System.out.println("----------------"+password);

            //判断原字符加密后和内容是否匹配

            boolean result = encoder.matches("123",password);

            System.out.println("======="+result);

        }

    }

    五.自定义登录逻辑

    当进行自定义登录逻辑时需要用到之前讲解的UserDetailsService和PasswordEncoder.但是Spring Security要求:当进行自定义登录时容器内必须有PasswordEncoder实例.所以补鞥呢直接new对象

    1.编写配置类

    新建类com.bjsxt.config.SecurityConfig编写下面内容

    @Configuration

    public class SecurityConfig{

        @Bean

        public PasswordEncoder getPwdEncoder(){

            return new BCryptPasswordEncoder();

        }

    }

    2.自定义逻辑

    在Spring Security中实现UserDetailService就表示为用户详情服务.在这个类中编写用户认证逻辑

    @Service

    public class UserDetailsServiceImpl implements userDetailsService{

        @Autowired

        private PasswordEncoder encoder;

        @Override

        public UserDetails loadUserByUsername(String username)throws UsernameNotFoundException{

            //1.查询数据库判断用户名是否存在,如果不存在抛出UsernameNotFoundException

            if(!username.equals("admin")){

                throw new UsernameNotFoundException("用户名不存在");

            }

                //把查询出来的面膜进行解析,或直接把password放到构造方法中

                //理解:password就是数据库中查询出来的密码,查询出来的内容不是123

                String password = encoder.encode("123");

                return new User(username,password,AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));

        }

    }

    3.查看效果

    重启项目后,在浏览器中输入账号:admin,密码:123.后可以正确进入到login.html页面

    六.自定义登录逻辑(数据库访问方式)

    1.新建数据库表结构

    根据RBAC设计思想完成数据库原型设计

    因为Spring Security中UserDetails的实现类是User,所以我们尽量不要叫做User

    create table users(

        id bigint primary key auto_increment,

        username varchar(20) unique not null,

        password varchar(100)

    );

    --密码zs

    insert into users values(1,'张三','$2a$10$dwi9Xv9cFDC1r8zQDp9wzupxoULvlzjtAMoes1zExZuDdLqtxT.rG');

    --密码ls

    insert into users values(2,'李四','$2a$10$Tomc5i8yHA.dUROgqX0eVO.Aa9qOAnvbNkUJhZ1znemqhRWdGGSle');

    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 vatchar(100),

        parentid bigint,

        permission varchar(20)

    );

    insert into menu values(1,'系统管理','0','menu:sys');

    insert into menu values(2,'用户管理','0','menu:user');

    create table role_menu(

        mid bigint,

        rif bigint

    );

    insert into 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);

    2.在项目中添加依赖

    添加MyBastis相关依赖

    <parent>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-parent</artifactId>

        <version>2.2.7.RELEASE</version>

    </parent>

    <dependencies>

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-security</artifactId>

        </dependency>

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-security</artifactId>

        </dependency>

        <dependency>

            <groupId>org.springframework.boot</groupId>

            <artifactId>spring-boot-starter-web</artifactId>

        </dependency>

        <dependency>

            <groupId>org/mubatis.spring.boot</groupId>

            <artifactId>mybatis-spring-boot-starter</artifactId>

            <version>2.1.1</version>

        </dependency>

        <dependency>

            <groupId>mysql</groupId>

            <artifactId>mysql-connector-java</artifactId>

            <version>5.1.48</version>

        </dependency>

        <dependency>

            <groupId>org.projectlombok</groupId>

            <artifactId>lombok</artifactId>

            <version>1.18.12</version>

        </dependency>

    </dependencies>

    3.编写配置文件

    先进application.yml

    在配置文件中添加Mybatis配置

    spring:

        datasource:

            driver-class-name: com.mysql.jdbc.Driver

            url: jdbc:mysql://localhost:3306/distributed

            username: root

            password: root

    4.新建实体类

    新建com.bjsxt.pojo.Users

    @Data

    public class Users{

        private Long id;

        private String username;

        private String password;

    }

    5.新建配置类

    新建com.bjsxt.config.SecurityConfig

    @Configuration

    public class Security{

        @Bean

        public PasswordEncoder passwordEncoder(){

    return new BCryptPasswordEncoder();

        }

    }

    6.新建Mapper

    新建com.bjsxt.mapper.UserMapper

    @Mapper

    public interface UserMapper{

        @Select("select*from users where username=#{username}")

        Users selectByUsername(String username);

    }

    7.修改自定义service逻辑

    修改com.bjsxt.service.UsersServiceImpl

    @Service

    public class UsersServiceImpl implements UserDetailsService{

        @Autowired

        private UserMapper userMapper;

        @Overirde

        public userDetails loadUserByUsername(String username)throws UsernameNotFoundException{

            Users users = usersMapper.selectByUsername(username);

            if(users==null){

                throw new UsernameNotFoundException("用户名不存在");

            }

            return new User(username,users.getPassword(),AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));

        }

    }

    8.先进启动器

    先进com.bjsxt.SecurityApplication

    @SpringBootApplication

    public class SecurityApplication{

        public static void main(String[] args){

            SpringApplication.run(SecurityApplication.class,args);

        }

    }

    七.自定义登录页面

    虽然Spring Security给我们提供了登录页面,但是对于实际项目中,大多喜欢使用自己的登录页面.所以Spring Security中不仅仅提供了登录页面.还支持用户自定义登录页面.实现过程也比较简单,只需要修改配置类即可

    说明:在上面代码基础上进行修改

    1.修改pom

    添加thymeleaf的依赖

    <dependency>

        <groupId>org.springframework.boot</groupId>

        <artifactId>spring-boot-starter-thymeleaf</artifactId>

    </dependency>

    2.编写登录页面

    在resource下先进templates文件夹.其中fail.html和success.html都只有一句话,分别是:"登陆失败"和"登录成功"

    编写登录页面.新建login.html

    <!DOCTYPE html>

    <html lang="en">

    <head>

        <meta charset="UTF-8">

        <title>内容</title>

    </head>

    <body>

    <form action="/login" method="post">

        <input type="text" name="username"/>

        <input type="password" name="password"/>

        <input type="submit" value="提交"/>

    </form>

    </body>

    </html>

    3.修改配置类

    修改配置类中主要是设置那个页面是登录页面.配置类需要继承WebSecurityConfigurerAdapter,并重写configure方法

        successForwardUrl()登录成功后跳转地址

        loginPage()登录页面

        loginProcessingUrl登录页面表单提交地址,此地址可以不真实存在

        antMatchers():匹配内容

        permitAll():允许

        注意:configure方法中除了FailureForwardurl()以外其他配置都是必须写的配置

    @Configuration

    public class SecurityConfig extends WebSecurityConfigurerAdapter{

        @Override

        protected void configure(HttpSecurity http)throws Exception{

            //配置认证

            http.formLogin()

                //哪个URL为登录页面

                .loginPage("/")

                //当发现什么URL是执行登录逻辑

                .loginProcessingUrl("/login")

                //成功后跳转到哪里

                .successForwardUrl("/success");

                //失败后跳转到哪里

                .failureFirwardUrl("/fail");

            //设置URL的授权问题

            //多个条件取交集

            http.authorizeRequests()

                //匹配/控制器 permitAll() 不需要被认证就可以访问

                .antMatchers("/").permitAll()

                //anyRequest()所有请求 authenticated()必须被认证

                .anyRequest().authenticated();

            //关闭csrf

            http.csrf().disable();

        }

        @Bean

        public PasswordEncoder passwordEncoder(){

            return new BCryptPasswordEncoder();

        }

    }

    4.编写控制器

    新建com.bjsxt.controller.UserController

    三个方法都是只有现实页面的功能.因为Thymeleaf页面必须通过控制器显示.如果实力代码时那纯HTML静态页演示,是不需要写这些控制器

    @Controller

    public class UserController{

        @RequestMapping("/")

        public String showLogin(){

            return "login";

        }

        @RequestMapping("/success")

        public String success(){

            return "success";

        }

        @RequestMapping("/fail")

        public String fail(){

            return "fail";

        }

    }

    5.测试效果

    在浏览器输入:http://localhost:8080显示登录页面.输入:账号:张三,密码:zs后会显示"登录成功"

    八.认证过程其他常用配置

    1.设置请求账户和密码的参数名

    1.1源码简介

    当进行登录时会执行UsernamePasswordAuthenticationFilter过滤器

    usernamePasrameter:账户参数名

    passwordParameter:面膜参数名

    postOnly=true:默认情况下只允许Post请求

    1.2修改配置

    //表单认证

    http.formLogin()

        .loginProcessingUrl("/login")    //当发现/login时认为是登录,需要执行UserDetailsServiceImpl

        .successFormwardUrl("/toMain")    //此处使post请求

        .failureForwardUrl("/fail")    //登录失败跳转地址

        .loginPage("/login.html")

        .usernameParameter("myusername")

        .passwordParameter("mypassword");

    1.3修改页面

    修改login.html

    <form action="/login" method="post">

        用户名:<input type="text" name="myusername"/><br/>

        密码:<input type="submit" value="登录"/>

    </form>

    2.登录成功三种配置方式

    2.1转发源码分析

    使用successForwardUrl()时表示成功后转发请求到地址.内部是通过successHandler()方法进行空值成功后交给哪个类进行处理

    ForwardAuthenticationSuccessHandler内部就是最简单的请求妆发.由于是请求转发,当遇到需要跳转到站外或在前后端分离的项目中就无法使用了

    当配置类使用.successHandler自定义成功逻辑

    .successHandler(new AuthenticationSuccessHandler){

        @Override

        public void onAuthenticationSuccess(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse,Authentication authentication)throws IOException,ServletException{

            httpServletResponse.sendRedirect("http://www.bjsxt.com");

        }

    }

    2.3自定义成功逻辑

    在配置类使用.successHandler自定义成功逻辑

    .successHandler(new AuthenticationSuccessHandler){

        @Override

        public void onAuthenticationSuccess(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse,Authentication authentication)throws IOException,ServletException{

            httpServletResponse.sendRedirect("http://www.bjsxt.com");

        }

    }

    2.3自定义成功逻辑

    在配置类使用.successHandler自定义成功逻辑

    .successHandler(new AuthenticationSuccessHandler()){

        @Override

        public void onAuthenticationSuccess(HttpServletRequest httpServletRequest,HttpServletRequest httpServletResponse,Authentication authentication)throws IOException,ServletException{

            httpServletResponse.sendRedirect("http://www.bjsxt.com");

        }

    }

    2.3重定向方法支持

    可以直接使用defaultSuccessUrl();可以进行重定向到特定页面

    .defaultSuccess("/success123")

    3.登录失败时三种配置方式

    3.1转发源码分析

    failureForwardUrl()内部调用的是failureHandler()方法

    ForwardAuthenticationFailureHandler中也是一个请求转发,并在request作用域中设置SPRING_SECURITY_LAST_EXCEPTION的key,内容为异常对象

    3.2自定义登录失败逻辑

    在配置类中使用.failureHandler设置

    .failureHandler(new AuthenticationFailureHandler(){

        @Override

        public void onAuthenticationFailure(HttpServletRequest httpServlet,HttpServletResponse httpServletResponse,AuthenticationException e)throws IOException,ServletException{

        httpServletResponse.sendRedirect("http://www.bjsxt.com");

    }

    })

    3.3登录失败后重定向到指定地址

    .failureUrl("http://www.root.com");

    九.完整认证流程(包含自定义页面和自定义登录逻辑)

    1.用户在浏览器中随意输入一个URL

    2.Spring Security会判断当前是否已经被认证,正常访问URL.如果没有被认证跳转到loginPage()对应的URL中,显示登录页面

    3.用户输入用户名和密码点击登录按钮后,发送登录url

    4.如果url和loginProcessingUrl()一样才执行登录流程.否则需要重新认证.

    5.执行登录流程时首先被UsernamPasswordAuthenticationFilter进行过滤,取出用户名和密码,如果没有配置这两个方法,默认为请求参数名username和password

    6.执行自定义登录逻辑UserDetailsService的实现类.判断用户名是否存在和数据库中,如果不存在,直接抛出UsernameNotFoundException.如果用户名存在,把从数据库中查询出来的密码通过org.springframework.security.core.userdetails.User传递给Spring Security.Spring Security根据容器中配置的Password encoder示例把客户端传递过来的密码和数据库传递过来的密码进行匹配.如果匹配成功表示认证成功

    7.如果登录成功,跳转到successForwardUrl(转发)/successHandler(自己控制跳转方式)/defaultSuccessUrl(重定向)配置的URL

    8.如果登录失败,跳转到failureForwardUrl/failureHandler/failureUrl

    相关文章

      网友评论

          本文标题:高级框架第七天Spring Security:安全管理框架

          本文链接:https://www.haomeiwen.com/subject/bjgesktx.html