Spring-boot结合Shrio实现JWT的方法
程序员文章站
2023-12-15 18:48:52
本文介绍了spring-boot结合shrio实现jwt的方法,分享给大家,具体如下:
关于验证大致分为两个方面:
用户登录时的验证;
用户登录后每次访问...
本文介绍了spring-boot结合shrio实现jwt的方法,分享给大家,具体如下:
关于验证大致分为两个方面:
- 用户登录时的验证;
- 用户登录后每次访问时的权限认证
主要解决方法:使用自定义的shiro filter
项目搭建:
这是一个spring-boot 的web项目,不了解spring-boot的项目搭建,请google。
pom.mx引入相关jar包
<!-- shiro 权限管理 --> <dependency> <groupid>org.apache.shiro</groupid> <artifactid>shiro-spring</artifactid> <version>${shiro.version}</version> </dependency> <dependency> <groupid>org.apache.shiro</groupid> <artifactid>shiro-core</artifactid> <version>${shiro.version}</version> </dependency> <!-- jwt --> <dependency> <groupid>io.jsonwebtoken</groupid> <artifactid>jjwt</artifactid> <version>0.9.0</version> </dependency>
shrio 的相关配置
划重点!!自定义了一个filter
filtermap.put("jwtfilter", new jwtfilter());
@configuration public class shiroconfig { @bean public shirofilterfactorybean getshirofilterfactorybean(securitymanager securitymanager) { shirofilterfactorybean shirofilterfactorybean = new shirofilterfactorybean(); shirofilterfactorybean.setsecuritymanager(securitymanager); // 添加自己的过滤器并且取名为jwtfilter map<string, filter> filtermap = new hashmap<>(); filtermap.put("jwtfilter", new jwtfilter()); shirofilterfactorybean.setfilters(filtermap); /* * 自定义url规则 * http://shiro.apache.org/web.html#urls- */ map<string, string> filterchaindefinitionmap = shirofilterfactorybean.getfilterchaindefinitionmap(); filterchaindefinitionmap.put("/**", "jwtfilter"); shirofilterfactorybean.setfilterchaindefinitionmap(filterchaindefinitionmap); return shirofilterfactorybean; } /** * securitymanager 不用直接注入shirodbrealm,可能会导致事务失效 * 解决方法见 handlecontextrefresh * http://www.debugrun.com/a/nks9ejq.html */ @bean("securitymanager") public defaultwebsecuritymanager securitymanager(tokenrealm tokenrealm) { defaultwebsecuritymanager manager = new defaultwebsecuritymanager(); manager.setrealm(tokenrealm); /* * 关闭shiro自带的session,详情见文档 * http://shiro.apache.org/session-management.html#sessionmanagement-statelessapplications%28sessionless%29 */ defaultsubjectdao subjectdao = new defaultsubjectdao(); defaultsessionstorageevaluator defaultsessionstorageevaluator = new defaultsessionstorageevaluator(); defaultsessionstorageevaluator.setsessionstorageenabled(false); subjectdao.setsessionstorageevaluator(defaultsessionstorageevaluator); manager.setsubjectdao(subjectdao); return manager; } @bean public lifecyclebeanpostprocessor lifecyclebeanpostprocessor() { return new lifecyclebeanpostprocessor(); } @bean(name = "tokenrealm") @dependson("lifecyclebeanpostprocessor") public tokenrealm tokenrealm() { return new tokenrealm(); } @bean @dependson("lifecyclebeanpostprocessor") public defaultadvisorautoproxycreator defaultadvisorautoproxycreator() { defaultadvisorautoproxycreator defaultadvisorautoproxycreator = new defaultadvisorautoproxycreator(); // 强制使用cglib,防止重复代理和可能引起代理出错的问题 // https://zhuanlan.zhihu.com/p/29161098 defaultadvisorautoproxycreator.setproxytargetclass(true); return defaultadvisorautoproxycreator; } @bean public authorizationattributesourceadvisor getauthorizationattributesourceadvisor(securitymanager securitymanager) { authorizationattributesourceadvisor authorizationattributesourceadvisor = new authorizationattributesourceadvisor(); authorizationattributesourceadvisor.setsecuritymanager(securitymanager); return new authorizationattributesourceadvisor(); } }
自定义shrio filter
执行顺序:prehandle -> dofilterinternal -> executelogin -> onloginsuccess
主要判断是不是登录请求的是 dofilterinternal
public class jwtfilter extends basichttpauthenticationfilter { /** * 自定义执行登录的方法 */ @override protected boolean executelogin(servletrequest request, servletresponse response) throws ioexception { httpservletrequest httpservletrequest = (httpservletrequest) request; usernamepasswordtoken usernamepasswordtoken = json.parseobject(httpservletrequest.getinputstream(), usernamepasswordtoken.class); // 提交给realm进行登入,如果错误他会抛出异常并被捕获 subject subject = this.getsubject(request, response); subject.login(usernamepasswordtoken); return this.onloginsuccess(usernamepasswordtoken, subject, request, response); //错误抛出异常 } /** * 最先执行的方法 */ @override protected boolean prehandle(servletrequest request, servletresponse response) throws exception { return super.prehandle(request, response); } /** * 登录成功后登录的操作 * 加上jwt 的header */ @override protected boolean onloginsuccess(authenticationtoken token, subject subject, servletrequest request, servletresponse response) { httpservletresponse httpservletresponse = (httpservletresponse) response; string jwttoken = jwts.builder() .setid(token.getprincipal().tostring()) .setexpiration(datetime.now().plusminutes(30).todate()) .signwith(signaturealgorithm.hs256, jwtcost.signaturekey) .compact(); httpservletresponse.addheader(authorization_header, jwttoken); return true; } /** * 登录以及校验的主要流程 * 判断是否是登录,或者是登陆后普通的一次请求 */ @override public void dofilterinternal(servletrequest servletrequest, servletresponse servletresponse, filterchain filterchain) throws ioexception, servletexception { httpservletrequest httpservletrequest = (httpservletrequest) servletrequest; httpservletresponse httpservletresponse = (httpservletresponse) servletresponse; string servletpath = httpservletrequest.getservletpath(); if (stringutils.equals(servletpath, "/login")) { //执行登录 this.executelogin(servletrequest, servletresponse); } else { string authenticationheader = httpservletrequest.getheader(authorization_header); if (stringutils.isnotempty(authenticationheader)) { claims body = jwts.parser() .setsigningkey(jwtcost.signaturekey) .parseclaimsjws(authenticationheader) .getbody(); if (body != null) { //更新token body.setexpiration(datetime.now().plusminutes(30).todate()); string updatetoken = jwts.builder().setclaims(body).compact(); httpservletresponse.addheader(authorization_header, updatetoken); //添加用户凭证 principalcollection principals = new simpleprincipalcollection(body.getid(), jwtcost.usernamepasswordrealm);//拼装shiro用户信息 websubject.builder builder = new websubject.builder(servletrequest, servletresponse); builder.principals(principals); builder.authenticated(true); builder.sessioncreationenabled(false); websubject subject = builder.buildwebsubject(); //塞入容器,统一调用 threadcontext.bind(subject); filterchain.dofilter(httpservletrequest, httpservletresponse); } } else { httpservletresponse.setstatus(httpstatus.forbidden.value()); } } } }
登录失败处理
处理shrio异常
@restcontrolleradvice public class globalcontrollerexceptionhandler { @exceptionhandler(value = exception.class) public object allexceptionhandler(httpservletrequest request, httpservletresponse response, exception exception) { string message = exception.getcause().getmessage(); logutil.error(message); return new resultinfo(exception.getclass().getname(), message); } /*=========== shiro 异常拦截==============*/ @exceptionhandler(value = incorrectcredentialsexception.class) public string incorrectcredentialsexception(httpservletrequest request, httpservletresponse response, exception exception) { response.setstatus(httpstatus.forbidden.value()); return "incorrectcredentialsexception"; } @exceptionhandler(value = unknownaccountexception.class) public string unknownaccountexception(httpservletrequest request, httpservletresponse response, exception exception) { response.setstatus(httpstatus.forbidden.value()); return "unknownaccountexception"; } @exceptionhandler(value = lockedaccountexception.class) public string lockedaccountexception(httpservletrequest request, httpservletresponse response, exception exception) { response.setstatus(httpstatus.forbidden.value()); return "lockedaccountexception"; } @exceptionhandler(value = excessiveattemptsexception.class) public string excessiveattemptsexception(httpservletrequest request, httpservletresponse response, exception exception) { response.setstatus(httpstatus.forbidden.value()); return "excessiveattemptsexception"; } @exceptionhandler(value = authenticationexception.class) public string authenticationexception(httpservletrequest request, httpservletresponse response, exception exception) { response.setstatus(httpstatus.forbidden.value()); return "authenticationexception"; } @exceptionhandler(value = unauthorizedexception.class) public string unauthorizedexception(httpservletrequest request, httpservletresponse response, exception exception) { response.setstatus(httpstatus.forbidden.value()); return "unauthorizedexception"; } }
处理jwt异常
这是个坑,因为是在filter内发生的异常,@exceptionhandler是截获不到的。
/** * 截获spring boot error页面 */ @restcontroller public class globalexceptionhandler implements errorcontroller { @override public string geterrorpath() { return "/error"; } @requestmapping(value = "/error") public object error(httpservletrequest request, httpservletresponse response) throws exception { // 错误处理逻辑 exception exception = (exception) request.getattribute("javax.servlet.error.exception"); throwable cause = exception.getcause(); if (cause instanceof expiredjwtexception) { response.setstatus(httpstatus.gateway_timeout.value()); return new resultinfo("expiredjwtexception", cause.getmessage()); } if (cause instanceof malformedjwtexception) { response.setstatus(httpstatus.forbidden.value()); return new resultinfo("malformedjwtexception", cause.getmessage()); } return new resultinfo(cause.getcause().getmessage(), cause.getmessage()); } }
关于权限等授权信息,可以直接放到redis中实现缓存。我认为也是不错的。
源码奉上::温馨提示:平时测试代码可能比较乱。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。