javascript
springboot 整合security——自定义表单登录 -凯发ag旗舰厅登录网址下载
文章目录
- 一、添加验证码
- 1.1 验证servlet
- 1.2 修改 login.html
- 1.3 添加匿名访问 url
- 二、ajax 验证
- 三、过滤器验证
- 3.1 编写验证码过滤器
- 3.2 注入过滤器
- 3.3 运行程序
- 四、spring security 验证
- 4.1 webauthenticationdetails
- 4.2 authenticationdetailssource
- 4.3 authenticationprovider
- 4.4 运行程序
通过前面三篇文章,你应该大致了解了 spring security 的流程。你应该发现了,真正的 login 请求是由 spring security 帮我们处理的,那么我们如何实现自定义表单登录呢,比如添加一个验证码…
一、添加验证码
1.1 验证servlet
public class verifyservlet extends httpservlet {/*** 验证码图片的宽度。*/private int width = 100;/*** 验证码图片的高度。*/private int height = 30;/*** 验证码字符个数*/private int codecount = 4;/*** 字体高度*/private int fontheight;/*** 干扰线数量*/private int interline = 16;/*** 第一个字符的x轴值,因为后面的字符坐标依次递增,所以它们的x轴值是codex的倍数*/private int codex;/*** codey ,验证字符的y轴值,因为并行所以值一样*/private int codey;/*** codesequence 表示字符允许出现的序列值*/char[] codesequence = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j','k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w','x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };/*** 初始化验证图片属性*/@overridepublic void init() throws servletexception {// 从web.xml中获取初始信息// 宽度string strwidth = this.getinitparameter("width");// 高度string strheight = this.getinitparameter("height");// 字符个数string strcodecount = this.getinitparameter("codecount");// 将配置的信息转换成数值try {if (strwidth != null && strwidth.length() != 0) {width = integer.parseint(strwidth);}if (strheight != null && strheight.length() != 0) {height = integer.parseint(strheight);}if (strcodecount != null && strcodecount.length() != 0) {codecount = integer.parseint(strcodecount);}} catch (numberformatexception e) {e.printstacktrace();}//width-4 除去左右多余的位置,使验证码更加集中显示,减得越多越集中。//codecount 1 //等比分配显示的宽度,包括左右两边的空格codex = (width-4) / (codecount 1);//height - 10 集中显示验证码fontheight = height - 10;codey = height - 7;}/*** @param request* @param response* @throws servletexception* @throws java.io.ioexception*/@overrideprotected void service(httpservletrequest request, httpservletresponse response) throws servletexception, java.io.ioexception {// 定义图像bufferbufferedimage buffimg = new bufferedimage(width, height, bufferedimage.type_int_rgb);graphics2d gd = buffimg.creategraphics();// 创建一个随机数生成器类random random = new random();// 将图像填充为白色gd.setcolor(color.light_gray);gd.fillrect(0, 0, width, height);// 创建字体,字体的大小应该根据图片的高度来定。font font = new font("times new roman", font.plain, fontheight);// 设置字体。gd.setfont(font);// 画边框。gd.setcolor(color.black);gd.drawrect(0, 0, width - 1, height - 1);// 随机产生16条干扰线,使图象中的认证码不易被其它程序探测到。gd.setcolor(color.gray);for (int i = 0; i < interline; i ) {int x = random.nextint(width);int y = random.nextint(height);int xl = random.nextint(12);int yl = random.nextint(12);gd.drawline(x, y, x xl, y yl);}// randomcode用于保存随机产生的验证码,以便用户登录后进行验证。stringbuffer randomcode = new stringbuffer();int red = 0, green = 0, blue = 0;// 随机产生codecount数字的验证码。for (int i = 0; i < codecount; i ) {// 得到随机产生的验证码数字。string strrand = string.valueof(codesequence[random.nextint(36)]);// 产生随机的颜色分量来构造颜色值,这样输出的每位数字的颜色值都将不同。red = random.nextint(255);green = random.nextint(255);blue = random.nextint(255);// 用随机产生的颜色将验证码绘制到图像中。gd.setcolor(new color(red,green,blue));gd.drawstring(strrand, (i 1) * codex, codey);// 将产生的四个随机数组合在一起。randomcode.append(strrand);}// 将四位数字的验证码保存到session中。httpsession session = request.getsession();session.setattribute("validatecode", randomcode.tostring());// 禁止图像缓存。response.setheader("pragma", "no-cache");response.setheader("cache-control", "no-cache");response.setdateheader("expires", 0);response.setcontenttype("image/jpeg");// 将图像输出到servlet输出流中。servletoutputstream sos = response.getoutputstream();imageio.write(buffimg, "jpeg", sos);sos.close();}}然后在 application 中注入该 servlet:
@springbootapplication public class application {public static void main(string[] args) {springapplication.run(application.class, args);}@beanpublic servletregistrationbean servletregistrationbean() {servletregistrationbean servletregistrationbean = new servletregistrationbean(new verifyservlet());servletregistrationbean.addurlmappings("/getverifycode");return servletregistrationbean;} }1.2 修改 login.html
在原本的 login 页面基础上加上验证码字段
1.3 添加匿名访问 url
在 websecurityconfig 中允许该 url 的匿名访问,不然没有登录是没有办法访问的:
@overrideprotected void configure(httpsecurity http) throws exception {http.authorizerequests()// 如果有允许匿名的url,填在下面.antmatchers("/getverifycode").permitall().anyrequest().authenticated().and().formlogin().loginpage("/login")// 设置登陆成功页.defaultsuccess.permitall().failurehandler(new simpleurlauthenticationfailurehandler())//登录失败url.failureforward.authenticationdetailssource(authenticationdetailssource).and()// 自定义登陆用户名和密码参数,默认为username和password// .usernameparameter("username")// .passwordparameter("password").logout().permitall()//基于内存自动登录.and().rememberme().tokenrepository(persistenttokenrepository()).tokenvalidityseconds(60) ;http.csrf().disable();}这样验证码就加好了,运行下程序:
下面才算是这篇文章真正的部分。我们如何才能实现验证码验证呢,思考一下,应该有以下几种实现方式:
二、ajax 验证
使用 ajax 方式验证和我们 spring security 框架就没有任何关系了,其实就是表单提交前先发个 http 请求验证验证码,本篇不再赘述。
三、过滤器验证
使用过滤器的思路是:在 spring security 处理登录验证请求前,验证验证码,如果正确,放行;如果不正确,调到异常。
3.1 编写验证码过滤器
自定义一个过滤器,实现 onceperrequestfilter (该 filter 保证每次请求一定会过滤),在 isprotected 方法中拦截了 post 方式的 /login 请求。
在逻辑处理中从 request 中取出验证码,并进行验证,如果验证成功,放行;验证失败,手动生成异常。
//获取当前线程绑定的request对象
httpservletrequest request = ((servletrequestattributes) requestcontextholder.getrequestattributes()).getrequest();
string validatecode = ((string)request.getsession().getattribute(“validatecode”)).touppercase();
3.2 注入过滤器
修改 websecurityconfig 的 configure 方法,添加一个 addfilterbefore() ,具有两个参数,作用是在参数二之前执行参数一设置的过滤器。
spring security 对于用户名/密码登录方式是通过 usernamepasswordauthenticationfilter 处理的,我们在它之前执行验证码过滤器即可。
@override protected void configure(httpsecurity http) throws exception {http.authorizerequests()// 如果有允许匿名的url,填在下面.antmatchers("/getverifycode").permitall().anyrequest().authenticated().and()// 设置登陆页.formlogin().loginpage("/login")// 设置登陆成功页.defaultsuccess.permitall()// 登录失败url.failure// 自定义登陆用户名和密码参数,默认为username和password // .usernameparameter("username") // .passwordparameter("password").and().addfilterbefore(new verifyfilter(),usernamepasswordauthenticationfilter.class).logout().permitall()// 自动登录.and().rememberme().tokenrepository(persistenttokenrepository())// 有效时间:单位s.tokenvalidityseconds(60).userdetailsservice(userdetailsservice);// 关闭csrf跨域http.csrf().disable(); }3.3 运行程序
四、spring security 验证
使用过滤器就已经实现了验证码功能,但其实它和 ajax 验证差别不大。
- ajax 是在提交前发一个请求,请求返回成功就提交,否则不提交
- 过滤器是先验证验证码,验证成功就让 spring security 验证用户名和密码;验证失败,则产生异常·。
如果我们要做的需求是用户登录是需要多个验证字段,不单单是用户名和密码,那么使用过滤器会让逻辑变得复杂,这时候可以考虑自定义 spring security 的验证逻辑了…
4.1 webauthenticationdetails
我们知道 spring security 默认只会处理用户名和密码信息。这时候就要请出我们的主角——webauthenticationdetails
webauthenticationdetails: 该类提供了获取用户登录时携带的额外信息的功能,默认提供了 remoteaddress 与 sessionid 信息。
我们需要实现自定义的 webauthenticationdetails,并在其中加入凯发ag旗舰厅登录网址下载的验证码:
/*** 获取用户登录时携带的额外信息* @author shuliangzhao* @title: customwebauthenticationdetails* @projectname spring-boot-learn* @description: todo* @date 2019/8/4 17:14*/ public class customwebauthenticationdetails extends webauthenticationdetails {private final string verifycode;public customwebauthenticationdetails(httpservletrequest request) {super(request);// verifycode为页面中验证码的nameverifycode = request.getparameter("verifycode");}public string getverifycode() {return this.verifycode;}@overridepublic string tostring() {stringbuilder sb = new stringbuilder();sb.append(super.tostring()).append("; verifycode: ").append(this.getverifycode());return sb.tostring();} }在这个方法中,我们将前台 form 表单中的 verifycode 获取到,并通过 get 方法方便被调用。
4.2 authenticationdetailssource
自定义了webauthenticationdetails,我i们还需要将其放入 authenticationdetailssource 中来替换原本的 webauthenticationdetails ,因此还得实现自定义 authenticationdetailssource :
/*** 该接口用于在spring security登录过程中对用户的登录信息的详细信息进行填充* @author shuliangzhao* @title: customauthenticationdetailssource* @projectname spring-boot-learn* @description: todo* @date 2019/8/4 17:17*/ @component("authenticationdetailssource") public class customauthenticationdetailssource implements authenticationdetailssource该类内容将原本的 webauthenticationdetails 替换为了我们的 customwebauthenticationdetails。
然后我们将 customauthenticationdetailssource 注入spring security中,替换掉默认的 authenticationdetailssource。
修改 websecurityconfig,将其注入,然后在config()中使用 authenticationdetailssource(authenticationdetailssource)方法来指定它。
private authenticationdetailssource4.3 authenticationprovider
至此我们通过自定义webauthenticationdetails和authenticationdetailssource将验证码和用户名、密码一起带入了spring security中,下面我们需要将它取出来。
这里需要我们自定义authenticationprovider,需要注意的是,如果是我们自己实现authenticationprovider,那么我们就需要自己做密码校验了
/*** @author shuliangzhao* @title: customauthenticationprovider* @projectname spring-boot-learn* @description: todo* @date 2019/8/4 17:20*/ @component public class customauthenticationprovider implements authenticationprovider {@autowiredprivate customuserdetailsservice userdetailsservice;@overridepublic authentication authenticate(authentication authentication) throws authenticationexception {// 获取用户输入的用户名和密码string name = authentication.getname();string inputpassword = authentication.getcredentials().tostring();customwebauthenticationdetails details = (customwebauthenticationdetails) authentication.getdetails();string verifycode = details.getverifycode();if(!validateverify(verifycode)) {throw new disabledexception("验证码输入错误");}//userdetails为数据库中查询到的用户信息userdetails userdetails = userdetailsservice.loaduserbyusername(name);if (!userdetails.getpassword().equals(inputpassword)) {throw new badcredentialsexception("密码错误");}return new usernamepasswordauthenticationtoken(name, inputpassword, userdetails.getauthorities());}private boolean validateverify(string verifycode) {//获取当前线程绑定的request对象httpservletrequest request = ((servletrequestattributes) requestcontextholder.getrequestattributes()).getrequest();string validatecode = ((string)request.getsession().getattribute("validatecode")).touppercase();verifycode = verifycode.touppercase();system.out.println("验证码:" validatecode "用户输入:" verifycode);return validatecode.equals(verifycode);}@overridepublic boolean supports(class authentication) {// 这里不要忘记,和usernamepasswordauthenticationtoken比较return authentication.equals(usernamepasswordauthenticationtoken.class);}}最后在 websecurityconfig 中将其注入,并在 config 方法中通过 auth.authenticationprovider() 指定使用。
@autowiredprivate customauthenticationprovider customauthenticationprovider;@overrideprotected void configure(authenticationmanagerbuilder auth) throws exception {auth.authenticationprovider(customauthenticationprovider);}4.4 运行程序
是不是比较复杂,为了实现该需求自定义了 webauthenticationdetails、authenticationdetailssource、authenticationprovider,让我们运行一下程序,当输入错误验证码时:
总结
以上是凯发ag旗舰厅登录网址下载为你收集整理的springboot 整合security——自定义表单登录的全部内容,希望文章能够帮你解决所遇到的问题。
如果觉得凯发ag旗舰厅登录网址下载网站内容还不错,欢迎将凯发ag旗舰厅登录网址下载推荐给好友。
- 上一篇: springboot整合spring s
- 下一篇: