spring security自定义认证登录的全过程记录
spring security使用分类:
如何使用spring security,相信百度过的都知道,总共有四种用法,从简到深为:
1、不用数据库,全部数据写在配置文件,这个也是官方文档里面的demo;
2、使用数据库,根据spring security默认实现代码设计数据库,也就是说数据库已经固定了,这种方法不灵活,而且那个数据库设计得很简陋,实用性差;
3、spring security和acegi不同,它不能修改默认filter了,但支持插入filter,所以根据这个,我们可以插入自己的filter来灵活使用;
4、暴力手段,修改源码,前面说的修改默认filter只是修改配置文件以替换filter而已,这种是直接改了里面的源码,但是这种不符合oo设计原则,而且不实际,不可用。
本文主要介绍了关于spring security自定义认证登录的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧。
1.概要
1.1.简介
spring security是一种基于 spring aop 和 servlet 过滤器的安全框架,以此来管理权限认证等。
1.2.spring security 自定义认证流程
1)认证过程
生成未认证的authenticationtoken
↑(获取信息) (根据authenticationtoken分配provider) authenticationfilter -> authenticationmanager -> authenticationprovider ↓(认证) userdetails(一般查询数据库获取) ↓(通过) 生成认证成功的authenticationtoken ↓(存放) securitycontextholder
2)将authenticationfilter加入到security过滤链(资源服务器中配置),如:
http.addfilterbefore(authenticationfilter, abstractpreauthenticatedprocessingfilter.class)
或者:
http.addfilterafter(authenticationfilter, usernamepasswordauthenticationfilter.class)
2.以手机号短信登录为例
2.1.开发环境
- springboot
- spring security
- redis
2.2.核心代码分析
2.2.1.自定义登录认证流程
2.2.1.1.自定义认证登录token
/** * 手机登录token * * @author : catalpaflat */ public class mobileloginauthenticationtoken extends abstractauthenticationtoken { private static final long serialversionuid = springsecuritycoreversion.serial_version_uid; private static final logger logger = loggerfactory.getlogger(mobileloginauthenticationtoken.class.getname()); private final object principal; public mobileloginauthenticationtoken(string mobile) { super(null); this.principal = mobile; this.setauthenticated(false); logger.info("mobileloginauthenticationtoken setauthenticated ->false loading ..."); } public mobileloginauthenticationtoken(object principal, collection<? extends grantedauthority> authorities) { super(authorities); this.principal = principal; // must use super, as we override super.setauthenticated(true); logger.info("mobileloginauthenticationtoken setauthenticated ->true loading ..."); } @override public void setauthenticated(boolean authenticated) { if (authenticated) { throw new illegalargumentexception( "cannot set this token to trusted - use constructor which takes a grantedauthority list instead"); } super.setauthenticated(false); } @override public object getcredentials() { return null; } @override public object getprincipal() { return this.principal; } @override public void erasecredentials() { super.erasecredentials(); } }
注:
setauthenticated():判断是否已认证
- 在过滤器时,会生成一个未认证的authenticationtoken,此时调用的是自定义token的setauthenticated(),此时设置为false -> 未认证
- 在提供者时,会生成一个已认证的authenticationtoken,此时调用的是父类的setauthenticated(),此时设置为true -> 已认证
2.2.1.1.自定义认证登录过滤器
/** * 手机短信登录过滤器 * * @author : catalpaflat */ public class mobileloginauthenticationfilter extends abstractauthenticationprocessingfilter { private boolean postonly = true; private static final logger logger = loggerfactory.getlogger(mobileloginauthenticationfilter.class.getname()); @getter @setter private string mobileparametername; public mobileloginauthenticationfilter(string mobileloginurl, string mobileparametername, string httpmethod) { super(new antpathrequestmatcher(mobileloginurl, httpmethod)); this.mobileparametername = mobileparametername; logger.info("mobileloginauthenticationfilter loading ..."); } @override public authentication attemptauthentication(httpservletrequest request, httpservletresponse response) throws authenticationexception, ioexception, servletexception { if (postonly && !request.getmethod().equals(httpmethod.post.name())) { throw new authenticationserviceexception("authentication method not supported: " + request.getmethod()); } //get mobile string mobile = obtainmobile(request); //assemble token mobileloginauthenticationtoken authrequest = new mobileloginauthenticationtoken(mobile); // allow subclasses to set the "details" property setdetails(request, authrequest); return this.getauthenticationmanager().authenticate(authrequest); } /** * 设置身份认证的详情信息 */ private void setdetails(httpservletrequest request, mobileloginauthenticationtoken authrequest) { authrequest.setdetails(authenticationdetailssource.builddetails(request)); } /** * 获取手机号 */ private string obtainmobile(httpservletrequest request) { return request.getparameter(mobileparametername); } public void setpostonly(boolean postonly) { this.postonly = postonly; } }
注:attemptauthentication()方法:
- 过滤指定的url、httpmethod
- 获取所需请求参数数据封装生成一个未认证的authenticationtoken
- 传递给authenticationmanager认证
2.2.1.1.自定义认证登录提供者
/** * 手机短信登录认证提供者 * * @author : catalpaflat */ public class mobileloginauthenticationprovider implements authenticationprovider { private static final logger logger = loggerfactory.getlogger(mobileloginauthenticationprovider.class.getname()); @getter @setter private userdetailsservice customuserdetailsservice; public mobileloginauthenticationprovider() { logger.info("mobileloginauthenticationprovider loading ..."); } /** * 认证 */ @override public authentication authenticate(authentication authentication) throws authenticationexception { //获取过滤器封装的token信息 mobileloginauthenticationtoken authenticationtoken = (mobileloginauthenticationtoken) authentication; //获取用户信息(数据库认证) userdetails userdetails = customuserdetailsservice.loaduserbyusername((string) authenticationtoken.getprincipal()); //不通过 if (userdetails == null) { throw new internalauthenticationserviceexception("unable to obtain user information"); } //通过 mobileloginauthenticationtoken authenticationresult = new mobileloginauthenticationtoken(userdetails, userdetails.getauthorities()); authenticationresult.setdetails(authenticationtoken.getdetails()); return authenticationresult; } /** * 根据token类型,来判断使用哪个provider */ @override public boolean supports(class<?> authentication) { return mobileloginauthenticationtoken.class.isassignablefrom(authentication); } }
注:authenticate()方法
- 获取过滤器封装的token信息
- 调取userdetailsservice获取用户信息(数据库认证)->判断通过与否
- 通过则封装一个新的authenticationtoken,并返回
2.2.1.1.自定义认证登录认证配置
@configuration(springbeannameconstant.default_custom_mobile_login_authentication_security_config_bn) public class mobileloginauthenticationsecurityconfig extends securityconfigureradapter<defaultsecurityfilterchain, httpsecurity> { private static final logger logger = loggerfactory.getlogger(mobileloginauthenticationsecurityconfig.class.getname()); @value("${login.mobile.url}") private string defaultmobileloginurl; @value("${login.mobile.parameter}") private string defaultmobileloginparameter; @value("${login.mobile.httpmethod}") private string defaultmobileloginhttpmethod; @autowired private customymlconfig customymlconfig; @autowired private userdetailsservice customuserdetailsservice; @autowired private authenticationsuccesshandler customauthenticationsuccesshandler; @autowired private authenticationfailurehandler customauthenticationfailurehandler; public mobileloginauthenticationsecurityconfig() { logger.info("mobileloginauthenticationsecurityconfig loading ..."); } @override public void configure(httpsecurity http) throws exception { mobilepojo mobile = customymlconfig.getlogins().getmobile(); string url = mobile.geturl(); string parameter = mobile.getparameter().getmobile(); string httpmethod = mobile.gethttpmethod(); mobileloginauthenticationfilter mobileloginauthenticationfilter = new mobileloginauthenticationfilter(stringutils.isblank(url) ? defaultmobileloginurl : url, stringutils.isblank(parameter) ? defaultmobileloginurl : parameter, stringutils.isblank(httpmethod) ? defaultmobileloginhttpmethod : httpmethod); mobileloginauthenticationfilter.setauthenticationmanager(http.getsharedobject(authenticationmanager.class)); mobileloginauthenticationfilter.setauthenticationsuccesshandler(customauthenticationsuccesshandler); mobileloginauthenticationfilter.setauthenticationfailurehandler(customauthenticationfailurehandler); mobileloginauthenticationprovider mobileloginauthenticationprovider = new mobileloginauthenticationprovider(); mobileloginauthenticationprovider.setcustomuserdetailsservice(customuserdetailsservice); http.authenticationprovider(mobileloginauthenticationprovider) .addfilterafter(mobileloginauthenticationfilter, usernamepasswordauthenticationfilter.class); } }
注:configure()方法
实例化authenticationfilter和authenticationprovider
将authenticationfilter和authenticationprovider添加到spring security中。
2.2.2.基于redis自定义验证码校验
2.2.2.1.基于redis自定义验证码过滤器
/** * 验证码过滤器 * * @author : catalpaflat */ @component(springbeannameconstant.default_validate_code_filter_bn) public class validatecodefilter extends onceperrequestfilter implements initializingbean { private static final logger logger = loggerfactory.getlogger(validatecodefilter.class.getname()); @autowired private customymlconfig customymlconfig; @autowired private redistemplate<object, object> redistemplate; /** * 验证请求url与配置的url是否匹配的工具类 */ private antpathmatcher pathmatcher = new antpathmatcher(); public validatecodefilter() { logger.info("loading validatecodefilter..."); } @override protected void dofilterinternal(httpservletrequest request, httpservletresponse response, filterchain filterchain) throws servletexception, ioexception { string url = customymlconfig.getlogins().getmobile().geturl(); if (pathmatcher.match(url, request.getrequesturi())) { string deviceid = request.getheader("deviceid"); if (stringutils.isblank(deviceid)) { throw new customexception(httpstatus.not_acceptable.value(), "not deviceid in the head of the request"); } string codeparamname = customymlconfig.getlogins().getmobile().getparameter().getcode(); string code = request.getparameter(codeparamname); if (stringutils.isblank(code)) { throw new customexception(httpstatus.not_acceptable.value(), "not code in the parameters of the request"); } string key = systemconstant.default_mobile_key_pix + deviceid; smscodepo smscodepo = (smscodepo) redistemplate.opsforvalue().get(key); if (smscodepo.isexpried()){ throw new customexception(httpstatus.bad_request.value(), "the verification code has expired"); } string smscode = smscodepo.getcode(); if (stringutils.isblank(smscode)) { throw new customexception(httpstatus.bad_request.value(), "verification code does not exist"); } if (stringutils.equals(code, smscode)) { redistemplate.delete(key); //let it go filterchain.dofilter(request, response); } else { throw new customexception(httpstatus.bad_request.value(), "validation code is incorrect"); } }else { //let it go filterchain.dofilter(request, response); } } }
注:dofilterinternal()
自定义验证码过滤校验
2.2.2.2.将自定义验证码过滤器添加到spring security过滤器链
http.addfilterbefore(validatecodefilter, abstractpreauthenticatedprocessingfilter.class)
注:添加到认证预处理过滤器前
3.测试效果
最后附上源码地址:https://gitee.com/catalpaflat/springsecurity.git (本地下载)
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。
上一篇: Kali Linux配置临时IP
推荐阅读
-
spring security自定义认证登录的全过程记录
-
Spring security 自定义登录页的post请求错误405
-
spring security自定义认证登录的全过程记录
-
Springboot+Spring Security实现前后端分离登录认证及权限控制的示例代码
-
Spring Security 自定义短信登录认证的实现
-
详解Spring Security的formLogin登录认证模式
-
史上最简单的Spring Security教程(二):自定义登录页
-
史上最简单的Spring Security教程(九):自定义用户登录失败页面
-
详解Spring Security的formLogin登录认证模式
-
Spring Security 自定义短信登录认证的实现