在使用springsecurity中,大伙都知道默认的登录数据是通过key/value的形式来传递的,默认情况下不支持json格式的登录数据,如果有这种需求,就需要自己来解决,本文主要和小伙伴来聊聊这个话题。
基本登录方案
在说如何使用json登录之前,我们还是先来看看基本的登录吧,本文为了简单,springsecurity在使用中就不连接数据库了,直接在内存中配置用户名和密码,具体操作步骤如下:
创建spring boot工程
首先创建springboot工程,添加springsecurity依赖,如下:
|
1 2 3 4 5 6 7 8 |
<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> |
添加security配置
创建securityconfig,完成springsecurity的配置,如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
@configuration public class securityconfig extends websecurityconfigureradapter { @bean passwordencoder passwordencoder() { return new bcryptpasswordencoder(); } @override protected void configure(authenticationmanagerbuilder auth) throws exception { auth.inmemoryauthentication().withuser( "zhangsan" ).password( "$2a$10$2o4ewlrrfpebotfdotc0f.rpumk.3q3kvbhrx7xxkumlbgjoobs8q" ).roles( "user" ); }
@override public void configure(websecurity web) throws exception { }
@override protected void configure(httpsecurity http) throws exception { http.authorizerequests() .anyrequest().authenticated() .and() .formlogin() .loginprocessingurl( "/dologin" ) .successhandler( new authenticationsuccesshandler() { @override public void onauthenticationsuccess(httpservletrequest req, httpservletresponse resp, authentication authentication) throws ioexception, servletexception { respbean ok = respbean.ok( "登录成功!" ,authentication.getprincipal()); resp.setcontenttype( "application/json;charset=utf-8" ); printwriter out = resp.getwriter(); out.write( new objectmapper().writevalueasstring(ok)); out.flush(); out.close(); } }) .failurehandler( new authenticationfailurehandler() { @override public void onauthenticationfailure(httpservletrequest req, httpservletresponse resp, authenticationexception e) throws ioexception, servletexception { respbean error = respbean.error( "登录失败" ); resp.setcontenttype( "application/json;charset=utf-8" ); printwriter out = resp.getwriter(); out.write( new objectmapper().writevalueasstring(error)); out.flush(); out.close(); } }) .loginpage( "/login" ) .permitall() .and() .logout() .logouturl( "/logout" ) .logoutsuccesshandler( new logoutsuccesshandler() { @override public void onlogoutsuccess(httpservletrequest req, httpservletresponse resp, authentication authentication) throws ioexception, servletexception { respbean ok = respbean.ok( "注销成功!" ); resp.setcontenttype( "application/json;charset=utf-8" ); printwriter out = resp.getwriter(); out.write( new objectmapper().writevalueasstring(ok)); out.flush(); out.close(); } }) .permitall() .and() .csrf() .disable() .exceptionhandling() .accessdeniedhandler( new accessdeniedhandler() { @override public void handle(httpservletrequest req, httpservletresponse resp, accessdeniedexception e) throws ioexception, servletexception { respbean error = respbean.error( "权限不足,访问失败" ); resp.setstatus( 403 ); resp.setcontenttype( "application/json;charset=utf-8" ); printwriter out = resp.getwriter(); out.write( new objectmapper().writevalueasstring(error)); out.flush(); out.close(); } });
} } |
这里的配置虽然有点长,但是很基础,配置含义也比较清晰,首先提供bcryptpasswordencoder作为passwordencoder,可以实现对密码的自动加密加盐,非常方便,然后提供了一个名为 zhangsan 的用户,密码是 123 ,角色是 user ,最后配置登录逻辑,所有的请求都需要登录后才能访问,登录接口是 /dologin ,用户名的key是username,密码的key是password,同时配置登录成功、登录失败以及注销成功、权限不足时都给用户返回json提示,另外,这里虽然配置了登录页面为 /login ,实际上这不是一个页面,而是一段json,在logincontroller中提供该接口,如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
@restcontroller @responsebody public class logincontroller { @getmapping ( "/login" ) public respbean login() { return respbean.error( "尚未登录,请登录" ); } @getmapping ( "/hello" ) public string hello() { return "hello" ; } } |
这里 /login 只是一个json提示,而不是页面, /hello 则是一个测试接口。
ok,做完上述步骤就可以开始测试了,运行springboot项目,访问 /hello 接口,结果如下:
此时先调用登录接口进行登录,如下:
登录成功后,再去访问 /hello 接口就可以成功访问了。
使用json登录
上面演示的是一种原始的登录方案,如果想将用户名密码通过json的方式进行传递,则需要自定义相关过滤器,通过分析源码我们发现,默认的用户名密码提取在usernamepasswordauthenticationfilter过滤器中,部分源码如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
public class usernamepasswordauthenticationfilter extends abstractauthenticationprocessingfilter { public static final string spring_security_form_username_key = "username" ; public static final string spring_security_form_password_key = "password" ;
private string usernameparameter = spring_security_form_username_key; private string passwordparameter = spring_security_form_password_key; private boolean postonly = true ; public usernamepasswordauthenticationfilter() { super ( new antpathrequestmatcher( "/login" , "post" )); }
public authentication attemptauthentication(httpservletrequest request, httpservletresponse response) throws authenticationexception { if (postonly && !request.getmethod().equals( "post" )) { throw new authenticationserviceexception( "authentication method not supported: " + request.getmethod()); }
string username = obtainusername(request); string password = obtainpassword(request);
if (username == null ) { username = "" ; }
if (password == null ) { password = "" ; }
username = username.trim();
usernamepasswordauthenticationtoken authrequest = new usernamepasswordauthenticationtoken( username, password);
// allow subclasses to set the "details" property setdetails(request, authrequest);
return this .getauthenticationmanager().authenticate(authrequest); }
protected string obtainpassword(httpservletrequest request) { return request.getparameter(passwordparameter); }
protected string obtainusername(httpservletrequest request) { return request.getparameter(usernameparameter); } //... //... } |
从这里可以看到,默认的用户名/密码提取就是通过request中的getparameter来提取的,如果想使用json传递用户名密码,只需要将这个过滤器替换掉即可,自定义过滤器如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public class customauthenticationfilter extends usernamepasswordauthenticationfilter { @override public authentication attemptauthentication(httpservletrequest request, httpservletresponse response) throws authenticationexception { if (request.getcontenttype().equals(mediatype.application_json_utf8_value) || request.getcontenttype().equals(mediatype.application_json_value)) { objectmapper mapper = new objectmapper(); usernamepasswordauthenticationtoken authrequest = null ; try (inputstream is = request.getinputstream()) { map<string,string> authenticationbean = mapper.readvalue(is, map. class ); authrequest = new usernamepasswordauthenticationtoken( authenticationbean.get( "username" ), authenticationbean.get( "password" )); } catch (ioexception e) { e.printstacktrace(); authrequest = new usernamepasswordauthenticationtoken( "" , "" ); } finally { setdetails(request, authrequest); return this .getauthenticationmanager().authenticate(authrequest); } } else { return super .attemptauthentication(request, response); } } } |
这里只是将用户名/密码的获取方案重新修正下,改为了从json中获取用户名密码,然后在securityconfig中作出如下修改:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
@override protected void configure(httpsecurity http) throws exception { http.authorizerequests().anyrequest().authenticated() .and() .formlogin() .and().csrf().disable(); http.addfilterat(customauthenticationfilter(), usernamepasswordauthenticationfilter. class ); } @bean customauthenticationfilter customauthenticationfilter() throws exception { customauthenticationfilter filter = new customauthenticationfilter(); filter.setauthenticationsuccesshandler( new authenticationsuccesshandler() { @override public void onauthenticationsuccess(httpservletrequest req, httpservletresponse resp, authentication authentication) throws ioexception, servletexception { resp.setcontenttype( "application/json;charset=utf-8" ); printwriter out = resp.getwriter(); respbean respbean = respbean.ok( "登录成功!" ); out.write( new objectmapper().writevalueasstring(respbean)); out.flush(); out.close(); } }); filter.setauthenticationfailurehandler( new authenticationfailurehandler() { @override public void onauthenticationfailure(httpservletrequest req, httpservletresponse resp, authenticationexception e) throws ioexception, servletexception { resp.setcontenttype( "application/json;charset=utf-8" ); printwriter out = resp.getwriter(); respbean respbean = respbean.error( "登录失败!" ); out.write( new objectmapper().writevalueasstring(respbean)); out.flush(); out.close(); } }); filter.setauthenticationmanager(authenticationmanagerbean()); return filter; } |
将自定义的customauthenticationfilter类加入进来即可,接下来就可以使用json进行登录了,如下:
好了,本文就先介绍到这里,有问题欢迎留言讨论。 希望对大家的学习有所帮助,也希望大家多多支持。
原文链接:https://segmentfault测试数据/a/1190000018157525
查看更多关于SpringSecurity登录使用JSON格式数据的方法的详细内容...