Spring Security——基于表单登录认证原理及实现
基于表单的登录认证
一、默认安全验证
当我们项目里添加spring security依赖它就已经起作用了,启动项目访问时,会出现弹出框。spring security默认
采用basic模式认证。浏览器发送http报文请求一个受保护的资源,浏览器会弹出对话框让输入用户名和密码。并以用
户名:密码的形式base64加密,加入Http报文头部的Authorization(默认用户名为user,密码则是会在启动程序时后
台console里输出,每次都不一样)。后台获取Http报文头部相关认证信息,认证成功返回相应内容,失败则继续认证。
下面会详细介绍具体认证流程。
复制代码
二、基于表单的认证原理
基本认证流程:SecurityContextPersistenceFilter↓AbstractAuthenticationProcessingFilter↓UsernamePasswordAuthenticationFilter↓AuthenticationManager↓AuthenticationProvider↓userDetailsService↓userDetails↓认证通过↓SecurityContext↓SecurityContextHolder↓RememberMeServices↓AuthenticationSuccessHandler
SecurityContextPersistenceFilter会校验请求中session是否有SecurityContext,有放SecurityContextHolder
中,返回时校验SecurityContextHolder中是否有securityContext,有放session,从而实现认证信息在多个请求中共
享。
AbstractAuthenticationProcessingFilter中会调自身attemptAuthentication抽象方法,
UsernamePasswordAuthenticationFilter是其一个实现类。
复制代码
它从请求中获取账号密码后,构造了一个token,此时没有认证,随后会调用AuthenticationManager接口的
authenticate方法
复制代码
下面是其构造函数,我们可看到这个token没有认证
复制代码
public UsernamePasswordAuthenticationToken(Object principal, Object credentials) {super((Collection)null);this.principal = principal;this.credentials = credentials;this.setAuthenticated(false);}
复制代码
ProviderManager实现了AuthenticationManager接口,它获取所有provider后遍历调用其supports方法,去匹配哪
个provider支持之前申明的token,找到provider后调用authenticate方法。到provider才开始认证用户信息,表单登录的是DaoAuthenticationProvider其父类进行以下操作:
①调用userDetailsService接口的loadUserByUsername方法获取UserDetails用户信息
②检查UserDetails用户是否可用、是否账户没有过期、是否账户没有被锁定
③检查UserDetails密码是否正确
④检查UserDetails密码是否没有过期
⑤都通过会重新申明一个token,不过多了权限集合参数如下
复制代码
public UsernamePasswordAuthenticationToken(Object principal, Object credentials,Collection authorities) {super(authorities);this.principal = principal;this.credentials = credentials;super.setAuthenticated(true);}
复制代码
此时用户才被认证通过,上述步骤有一个错了会抛出AuthenticationException异常的子类,下面是其继承图:
复制代码
认证结果返回给AbstractAuthenticationProcessingFilter,将结果放到SecurityContextHolder的
SecurityContext中,若添加了记住我功能又会调用rememberMeServices来实现,最后调用
AuthenticationSuccessHandler接口成功处理。
复制代码
三、表单登录实现
在第二部分介绍具体原理,接下来实现自定义登录验证。首先编写一个config类继承WebSecurityConfigurerAdapter
类重写configure方法填写配置
复制代码
protected void configure(HttpSecurity http) throws Exception {http/*** 表单登录配置*/.formLogin() //表单登录.loginPage("/authentication/login.html") //自定义登录页面.loginProcessingUrl("/authentication/form") //与自定义登录页面处理路径一致
// http.httpBasic() basic模式弹出框登录.successHandler(myAuthenticationSuccessHandler) //自定义认证成功处理.failureHandler(myAuthenticationFailureHandler) //自定义认证失败处理.and()/*** 需要认证的请求配置,* 注:最为具体的请求路径放在前面,而最不具体的路径(如anyRequest())放在最后面。* 如果不这样做的话,那不具体的路径配置将会覆盖掉更为具体的路径配置*/.authorizeRequests()//允许这样请求通过,需要将登录所需路径配好,不然会一直重定向.antMatchers("/authentication/*").permitAll() .anyRequest().authenticated()//任何请求都需要认证.and().csrf().disable(); //关闭csrf防御机制}复制代码
自定义用户实现UserDetailsService接口重写loadUserByUsername方法
复制代码
@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {return new User(username, "123456", true, true,true, true, new ArrayList());}
复制代码
这里的User是security自己的,写死了密码为123456,正常应该根据用户名从数据库查出用户信息。当然密码也不可
能为明文,这里推荐使用BCryptPasswordEncoder加密,因为其每次加密都会生成随机盐加入字符串中。需要在之前申
明的config类添加即可
复制代码
@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}
复制代码
这四个boolean值分别表示是否可用、是否账户没有过期、是否密码没有过期、是否账户没有被锁定。构造函数
最后一个参数为该用户用哪些接口权限,同样也从数据库查,但是这个权限只会在登录时初始化一次,若我登录后修
改权限,则无法同步,后续介绍解决办法。接下来就是两个登录的自定义登录处理,成功的实现AuthenticationSuccessHandler接口,失败的则实现
AuthenticationFailureHandler接口
复制代码
@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException{response.setContentType("application/json;UTF-8");response.getWriter().println("{\"success\":true,\"result\":\"" + "登录成功" + "\"}");}@Overridepublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {response.setContentType("application/json;UTF-8");response.getWriter().println("{\"error\":true,\"message\":\"登录名或者密码错误\"}");}
复制代码
注意成功和失败最后的方法参数是不同的,一个是成功的认证信息,另一个失败抛出的认证异常json串的key可以
根据自身登录页面ajax处理结果的属性来定,必须保持一致。页面代码就不复制了,做一个简单html就行。
复制代码
四、Remember Me
remember me顾名思义就是记住我,在登录成功认证以后服务端会发送cookie给浏览器,同时把用户名、base64加密随
机序列、生成token存入persistent_logins表中。当我下次再访问时,会读取cookie中的token与数据库中的token做
对比,若一直能表示认证通过,会重新生成新的token存入数据库中,序列户不变,再重新生成新的cookie发给浏览器。
下面看具体源码:
复制代码
在第二部分说了登录认证成功后会调用rememberMeServices的loginSuccess方法,这是最后调用的方法,我们看
到了它构造了一个rememberMeToken,将其存入数据库,并发送cookie。
复制代码
当下次登录,会请求到RememberMeAuthenticationFilter,它调用rememberMeServices接口的autoLogin方法,
去对比cookie中的token与数据库中的token,又判断了token是否过期,验证通过会根据token中的用户名调用
userDetailsService的loadUserByUsername方法获取用户信息。最后根据用户信息构造
一个RememberMeAuthenticationToken认证成功。
复制代码
四、Remember Me实现
remember me实现较为简单,只需要在申明的config类中添加
复制代码
@Autowired
private DataSource dataSource;@Autowiredprivate UserDetailsService userDetailsService;@Beanpublic PersistentTokenRepository persistentTokenRepository() {JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();tokenRepository.setDataSource(dataSource);//tokenRepository.setCreateTableOnStartup(true);return tokenRepository;}
复制代码
在configure方法里加
复制代码
.rememberMe().tokenRepository(persistentTokenRepository()).tokenValiditySeconds(3600) //有效时间.userDetailsService(userDetailsService)
复制代码
五、代码地址
https://github.com/qumaoming/spring-security
复制代码
Spring Security——基于表单登录认证原理及实现相关推荐
- spring mvc 基于表单的认证过程及cookie应用和session管理
我们日常生活中都会接触到各种登录过程,基于表单的认证一般是将客户端发送过来的用户ID和密码与之前登录过的信息做匹配来进行认证的.这个过程我们都很清楚,不过HTTP协议是无状态协议,不能保存用户登陆的状 ...
- SpringBoot 整合Security——自定义表单登录
文章目录 一.添加验证码 1.1 验证servlet 1.2 修改 login.html 1.3 添加匿名访问 Url 二.AJAX 验证 三.过滤器验证 3.1 编写验证码过滤器 3.2 注入过滤器 ...
- shiro表单登录认证及退出(自定义form认证器)
博主地址:http://blog.csdn.net/zcl_love_wx 注意:此文是基于springMVC框架的,所以关于springMVC的配置这里不说,后面有时间专门写一个shiro整合spr ...
- Spring Security实现JDBC用户登录认证
在搭建博客后端服务框架时,我采用邮件注册+Spring Security登录认证方式,结合mysql数据库,给大家展示下具体是怎么整合的. 本篇是基于上一篇:spring boot实现邮箱验证码注册 ...
- spring security基于数据库的安全认证 配置
创建数据库 /* Navicat MySQL Data TransferSource Server : mysql3306 Source Server Version : 50542 Source H ...
- (chap8 确认访问用户身份的认证) 基于表单认证
1. 定义 基于表单的认证方法并不是在HTTP协议中定义的.客户端会向服务器上的Web应用程序发送登录信息( Credential ),按登录信息的验证结果认证. 根据Web应用程序的实际安装,提供的 ...
- Spring Security实现短信登录
文章目录 一.理论说明 1.1 用户名密码登录逻辑 1.2 短信验证码登录逻辑 二.代码实战 2.1 SmsAuthenticationToken 2.2 SmsAuthenticationFilte ...
- Spring Security默认的用户登录表单 页面源代码
Spring Security默认的用户登录表单 页面源代码 <html><head><title>Login Page</title></hea ...
- Spring Security实现用户名密码验证的原理
title: Spring Security date: 2019-08-05 16:40:27 categories: 后端 tags: 后端 权限管理 Spring Security Spring ...
- Spring Security Oauth2 JWT 实现用户认证授权功能
Spring Security Oauth2 JWT 一 用户认证授权 1. 需求分析 1.1 用户认证与授权 什么是用户身份认证? 用户身份认证即用户去访问系统资源时系统要求验证用户的身份信息,身份 ...
最新文章
- 按照文字内容动态设置TableViewCell的高度
- maven项目找不到pom.xml配置的dependency jar中的类问题解决方法
- 关于DiscuzNT 1.0
- 江计算机科学与技术,计算机科学与技术
- 寻找想改变人工智能的“大人物”!2019百度奖学金正式启动
- Boost:以协程的方式实现带有单个默认值的echo服务器的实例
- how is Fiori launchpad host name and port number determine
- UIWebView加载Loading...两种方法
- 盐城有哪些中专计算机学校,盐城有什么中专学校
- 五:Java+SpringBoot集成SwaggerUI
- 实习成长之路:MySQL四:深入浅出索引
- 分析与设计:员工管理系统
- excel培训机构_不会excel的,不是好地产人
- php中字符串分割函数是,php分割字符串的函数是什么
- Tensorflow nmt的整体结构
- Laravel——微信授权登陆
- ToolBoxFeatureDataZoomOpts:工具箱区域缩放配置项
- Android 高德地图marker位置刷新操作
- 第一件 免费杀毒软件下载
- Python Web学习笔记,电影网站
热门文章
- Java 获取日期间的日期 根据日期获取星期
- Android Studio failed to resolve:com.android.support.appcompat-v7:28+ 报错
- 算法面试题(python)——如何找出数组中出现一次的数
- 奇奇怪怪的东西(1)
- 深入理解HTTP协议—HTTP协议详解(真的很经典)
- 09-排序2 Insert or Merge
- 【05】AngularJS 指令
- 【BZOJ】【2049】【SDOI2008】洞穴勘测 Cave
- Hibernate Annotation 设置字段的默认值
- Android的NDK开发(2)————利用Android NDK编写一个简单的HelloWorld