springsecurity_Spring Security

Spring Security

一、 Spring Security 简介

1 概括

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

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

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

2 历史

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

二、 第一个 Spring Security 项目

创建一个SpringBoot项目,添加web和security启动器,见下面的依赖

1 导入依赖

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

<?xml version="1.0" encoding="UTF-8"?>

2创建main.html和login.html

Main:

<!DOCTYPE html>

Login.html:

<!DOCTYPE html>

3测试

  • 启动类本身创建SpringBoot项目就会有
  • 启动项目
  • 访问http://localhost:8080/login.html

v2-b8206257fa857d5df0f91222278ae1f7_b.jpg

导入 spring-boot-starter-security 启动器后,Spring Security 已经生效,默认拦截全部请求,如果用户没有登录,跳转到内置登录页面,就是上面这个默认的 username 为 user,password 打印在控制台中。

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

v2-9a7eb9fe623493ee6d11c0008e99ee85_b.jpg

三、 UserDetailsService 详解

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

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

v2-fb46f909bce8cc2187cef52cf3e52631_b.jpg

1 返回值

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

v2-915b74174cfb8dd4af952207b69a2e01_b.jpg

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

org.springframework.security.core.userdetails.User此处经常和系统中自己开发的 User 类弄混。

v2-c154d5f4ed76140b23b8980c39dc2c31_b.jpg

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

v2-60ee6a936c8bf94dcf9e5ac6f56381ea_b.jpg

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

username:用户名,传过来的

password:密码,数据库查出来的

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

v2-458fee6fb7c63009ef68c3f2d24afb85_b.jpg

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

authorities 里面的权限对于后面学习授权是很有必要的,包含的所有内容为此用户具有的权限,如果里面没有包含某个权限,而在做某个事情时必须包含某个权限则会出现 403。通常都是通过

AuthorityUtils.commaSeparatedStringToAuthorityList(“”) 来创建authorities 集合对象的。参数是一个字符串,多个权限使用逗号分隔。

2 方法参数

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

3 异常

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

四、 PasswordEncoder 密码解析器详解

Spring Security 要求容器中必须有 PasswordEncoder 实例。所以当自定义登录逻辑时要求必须给容器注入 PaswordEncoder 的 bean 对象

1 接口介绍

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

matches()验证从存储中获取的编码密码与编码后提交的原始密码是否匹配。如果密码匹配,则返回 true;如果不匹配,则返回 false。

第一个参数表示需要被解析的密码。第二个参数表示存储的密码。

upgradeEncoding():如果解析的密码能够再次进行解析且达到更

安全的结果则返回 true,否则返回 false。默认返回 false。

v2-31c7c03db7113f4d6e95b68bef6caced_b.jpg

2 内置解析器介绍

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

v2-443f68cf17526cbc7e6d9cb513174206_b.jpg

3 BCryptPasswordEncoder 简介

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

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

4 代码演示

在上面的项目中(只要是引入了Spring Security启动器的项目)进行测试

package 

运行:

v2-b37201c7f9b95f8db317186a4266d277_b.jpg

五、 自定义登录逻辑

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

1 编写配置类

在上面的项目中:新建类 com.bjsxt.config.SecurityConfig 编写下面内容

package 

2 自定义逻辑

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

package 

3 查看效果

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

六、 自定义登录页面

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

1 编写登录页面

编写登录页面,登录页面中<form>的 action 不编写对应控制器也可以。其实就是上面写的login.html

<!DOCTYPE html>

2 修改配置类

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

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

loginPage() 登录页面

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

antMatchers():匹配内容

permitAll():允许

package 

3 编写控制器

编写控制器,当用户登录成功后跳转 toMain 控制器。编写完成控制器后编写 main.html。页面中随意写上一句话表示 main.html 页面内容即可。而之前的/login 控制器方法是不执行的,所以可以删除了。

package 

4测试

  • 启动项目
  • 访问http://localhost:8080/main,然后就会跳转到自定义登陆页面login.html
  • 输入admin用户名和123的密码,然后就会跳转到main.html

v2-76a84d67059e793f34674f0f2071e226_b.jpg

七、 认证过程其他常用配置

1 失败跳转

表单处理中成功会跳转到一个地址,失败也可以跳转到一个地址中。

1.1编写页面

在 src/main/resources/static 下新建 fail.html 并编写如下内容

<!DOCTYPE html>

1.2修改表单配置

在配置方法中表单认证部分添加 failureForwardUrl()方法,表示登录失败跳转的 url。此处依然是 POST 请求,所以跳转到可以接收 POST请求的控制器/fail 中。

v2-e638a35242e61b66224d48e79dc1dce7_b.jpg

1.3添加控制器方法

在控制器类中添加控制器方法,方法映射路径/fail。此处要注意:

由于是 POST 请求访问/fail。所以如果返回值直接转发到 fail.html 中,及时有效果,控制台也会报警告,提示 fail.html 不支持 POST 访问方式

package 

1.4设置 fail.html 不需要认证

认证失败跳转到 fail.html 页面中,所以必须配置 fail.html 不需要被认证。需要修改配置类中内容

v2-38d9c22eac4fcdd9fc02b142f055e5cb_b.jpg

最后现在为止,配置类SecurityConfig变成了:

package 

1.5测试

  • 启动项目
  • 访问http://localhost:8080/login.html,随便输入用户名和密码使其登陆失败

v2-978e07a076a4b206717e6bcafbf2daae_b.jpg
  • 点击跳转,进入登陆页面

v2-3fd4ac9c9f412283838d6f2d54b70fdf_b.jpg

输入admin,123得以进入

v2-14ea73ca5dfd83383b9550b6f3258a0b_b.jpg

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

2.1源码简介

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

usernamePasrameter:账户参数名

passwordParameter:密码参数名

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

v2-b5b5fa2709c53e1d08a84e0dfe785aba_b.jpg

2.2修改配置

v2-6af91c713782e73e3f04524e2006080a_b.jpg
package com.bjsxt.springsecuritydemo.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
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(HttpSecurity http) throws Exception {
 //表单认证
 http.formLogin()
                .usernameParameter("username123")
                .passwordParameter("password123")
 //loginProcessingUrl 登录页面表单提交地址,此地址可以不真实存在
 .loginProcessingUrl("/login")   //当发现是/login时认为是登陆,需要执行UserDetailsServiceImpl中的那个方法
 .loginPage("/login.html")   //配置登陆页面
 .successForwardUrl("/toMain")   //此处是post请求
 .failureForwardUrl("/fail");     //登陆失败跳转地址

        //url 拦截
 http.authorizeRequests()
                .antMatchers("/login.html").permitAll()  //login.html不需要被认证
 .antMatchers("/fail.html").permitAll()
                .anyRequest().authenticated();  //所有的请求都必须被认证。必须登陆后才能访问

        //关闭csrf防护
 http.csrf().disable();
    }

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

2.3修改页面

修改 login.html

<!DOCTYPE html>

这样就可通过测试

3 自定义登录成功处理器

3.1源码分析

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

v2-d6f4e5cc3bd02bedc4b09899d472c3ec_b.jpg

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

v2-99773944136552abf5d8b1bbb8e1a5aa_b.jpg

当需要控制登录成功后去做一些事情时,可以进行自定义认证成功控制器。

3.2代码实现

3.2.1 自定义类

新建类 com.bjsxt.handler.MyAuthenticationSuccessHandler 编写如下:

package 

3.2.2 修改配置项

使用 successHandler()方法设置成功后交给哪个对象进行处理

package com.bjsxt.springsecuritydemo.config;

import com.bjsxt.springsecuritydemo.handler.MyAuthenticationSuccessHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
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(HttpSecurity http) throws Exception {
 //表单认证
 http.formLogin()
                .usernameParameter("username123")
                .passwordParameter("password123")
 //loginProcessingUrl 登录页面表单提交地址,此地址可以不真实存在
 .loginProcessingUrl("/login")   //当发现是/login时认为是登陆,需要执行UserDetailsServiceImpl中的那个方法
 .loginPage("/login.html")   //配置登陆页面
//                .successForwardUrl("/toMain")   //此处是post请求
                重定向到百度。这只是一个示例,具体需要看项目业务需求
 .successHandler(new MyAuthenticationSuccessHandler("http://www.baidu.com"))
                .failureForwardUrl("/fail");     //登陆失败跳转地址

        //url 拦截
 http.authorizeRequests()
                .antMatchers("/login.html").permitAll()  //login.html不需要被认证
 .antMatchers("/fail.html").permitAll()
                .anyRequest().authenticated();  //所有的请求都必须被认证。必须登陆后才能访问

        //关闭csrf防护
 http.csrf().disable();
    }

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

3.2.3测试

  • 启动项目
  • 访问http://localhost:8080/login.html,登陆admin,123
  • 进入百度首页,查看控制台

v2-1fa7be3e60de75b6913a95df1899a753_b.jpg
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值