模型分类 增加用户名和密码 基于内存的认证 导入依赖 创建一个 jwtuser 实现 userdetails 编写工具类生成令牌 编写拦截器 编写 userdetailsservice 的实现类 编写登录 最后配置 config 运行,返回 token 重写 usernamepasswordanthenticationfilter 配置 securityconfig 设计数据表 着重配置 springconfig 权限系统躲不开的概念,在shiro和spring security之间,你一般选啥?在前后端分离的项目中,你知道怎么spring security整合jwt么,来看看这篇文章哈!
思维导图如下:
rbac 全称为基于角色的权限控制,本段将会从什么是 rbac,模型分类,什么是权限,用户组的使用,实例分析等几个方面阐述 rbac.
思维导图
绘制思维导图如下什么是 rbac
rbac 全称为用户角色权限控制,通过角色关联用户,角色关联权限,这种方式,间阶的赋予用户的权限,如下图所示
对于通常的系统而言,存在多个用户具有相同的权限,在分配的时候,要为指定的用户分配相关的权限,修改的时候也要依次的对这几个用户的权限进行修改,有了角色这个权限,在修改权限的时候,只需要对角色进行修改,就可以实现相关的权限的修改。这样做增加了效率,减少了权限漏洞的发生。
模型分类 对于 rbac 模型来说,分为以下几个模型 分别是 rbac0,rbac1,rbac2,rbac3,这四个模型,这段将会依次介绍这四个模型,其中最常用的模型有 rbac0.
rbac0 rbac0 是最简单的 rbac 模型,这里面包含了两种。
用户和角色是多对一的关系,即一个用户只充当一种角色,一个角色可以有多个角色的担当。用户和角色是多对多的关系,即,一个用户可以同时充当多个角色,一个角色可以有多个用户。此系统功能单一,人员较少,这里举个栗子,张三既是行政,也负责财务,此时张三就有俩个权限,分别是行政权限,和财务权限两个部分。
rbac1 相对于 rbac0 模型来说,增加了子角色,引入了继承的概念。
rbac2 模型 这里 rbac2 模型,在 rbac0 模型的基础上,增加了一些功能,以及限制
角色互斥 即,同一个用户不能拥有两个互斥的角色,举个例子,在财务系统中,一个用户不能拥有会计员和审计这两种角色。
基数约束 即,用一个角色,所拥有的成员是固定的,例如对于 ceo 这种角色,同一个角色,也只能有一个用户。
先决条件 即,对于该角色来说,如果想要获得更高的角色,需要先获取低一级别的角色。举个栗子,对于副总经理和经理这两个权限来说,需要先有副总经理权限,才能拥有经理权限,其中副总经理权限是经理权限的先决条件。
运行时互斥 即,一个用户可以拥有两个角色,但是这俩个角色不能同时使用,需要切换角色才能进入另外一个角色。举个栗子,对于总经理和专员这两个角色,系统只能在一段时间,拥有其一个角色,不能同时对这两种角色进行操作。
rbac3 模型 即,rbac1,rbac2,两者模型全部累计,称为统一模型。
什么是权限 权限是资源的集合,这里的资源指的是软件中的所有的内容,即,对页面的操作权限,对页面的访问权限,对数据的增删查改的权限。举个栗子。对于下图中的系统而言,
拥有,计划管理,客户管理,合同管理,出入库通知单管理,粮食安全追溯,粮食统计查询,设备管理这几个页面,对这几个页面的访问,以及是否能够访问到菜单,都属于权限。
用户组的使用 对于用户组来说,是把众多的用户划分为一组,进行批量授予角色,即,批量授予权限。举个栗子,对于部门来说,一个部门拥有一万多个员工,这些员工都拥有相同的角色,如果没有用户组,可能需要一个个的授予相关的角色,在拥有了用户组以后,只需要,把这些用户全部划分为一组,然后对该组设置授予角色,就等同于对这些用户授予角色。
优点:减少工作量,便于理解,增加多级管理,等。
首先添加依赖
org.springframework.boot spring-boot-starter-security 然后添加相关的访问接口
package com.example.demo.web;import org.springframework.web.bind.annotation.requestmapping;import org.springframework.web.bind.annotation.restcontroller;@restcontroller@requestmapping(/test)public class test { @requestmapping(/test) public string test(){ return test; }} 最后启动项目,在日志中查看相关的密码
访问接口,可以看到相关的登录界面
输入用户名和相关的密码
用户名:user密码 984cccf2-ba82-468e-a404-7d32123d0f9c 登录成功
基于 spring boot + mybatis plus + vue & element 实现的后台管理系统 + 用户小程序,支持 rbac 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
项目地址:https://gitee.com/zhijiantianya/ruoyi-vue-pro 视频教程:https://doc.iocoder.cn/video/ 增加用户名和密码 在配置文件中,书写相关的登录和密码
spring: security: user: name: ming password: 123456 roles: admin 在登录页面,输入用户名和密码,即可正常登录
基于 spring cloud alibaba + gateway + nacos + rocketmq + vue & element 实现的后台管理系统 + 用户小程序,支持 rbac 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能
项目地址:https://gitee.com/zhijiantianya/yudao-cloud 视频教程:https://doc.iocoder.cn/video/ 基于内存的认证 需要自定义类继承 websecurityconfigureradapter 代码如下
package com.example.demo.config;import org.springframework.context.annotation.bean;import org.springframework.context.annotation.configuration;import org.springframework.security.config.annotation.authentication.builders.authenticationmanagerbuilder;import org.springframework.security.config.annotation.web.configuration.websecurityconfigureradapter;import org.springframework.security.crypto.password.nooppasswordencoder;import org.springframework.security.crypto.password.passwordencoder;@configurationpublic class mywebsecurityconfig extends websecurityconfigureradapter { @bean passwordencoder passwordencoder(){ return nooppasswordencoder.getinstance(); } @override protected void configure(authenticationmanagerbuilder auth) throws exception { auth.inmemoryauthentication() .withuser(admin).password(123).roles(admin); }} 即,配置的用户名为 admin,密码为 123,角色为 admin
httpsecurity
这里对一些方法进行拦截
package com.ming.demo.interceptor;@configuration@enablewebsecuritypublic class securityconfig extends websecurityconfigureradapter { @override public void configure(authenticationmanagerbuilder auth) throws exception { auth.inmemoryauthentication() .withuser(itguang).password(123456).roles(user).and() .withuser(admin).password({noop} + 123456).roles(admin); } @override protected void configure(httpsecurity http) throws exception { http.authorizerequests() .anyrequest().permitall() .and() .formlogin() .permitall() .and() .logout() .permitall(); }} 即,这里完成了对所有的方法访问的拦截。
这是一个小 demo,目的,登录以后返回 jwt 生成的 token
导入依赖 添加 web 依赖
导入 jwt 和 security 依赖
io.jsonwebtoken jjwt 0.9.1 org.springframework.boot spring-boot-starter-security 2.3.1.release 创建一个 jwtuser 实现 userdetails 创建 一个相关的 javabean
package com.example.demo;import org.springframework.security.core.grantedauthority;import org.springframework.security.core.userdetails.userdetails;import java.util.collection;public class jwtuser implements userdetails { private string username; private string password; private integer state; private collection authorities; public jwtuser(){ } public jwtuser(string username, string password, integer state, collection authorities){ this.username = username; this.password = password; this.state = state; this.authorities = authorities; } @override public collection getauthorities() { return authorities; } @override public string getpassword() { return this.password; } @override public string getusername() { return this.username; } @override public boolean isaccountnonexpired() { return true; } @override public boolean isaccountnonlocked() { return true; } @override public boolean iscredentialsnonexpired() { return true; } @override public boolean isenabled() { return true; }} 编写工具类生成令牌 编写工具类,用来生成 token,以及刷新 token,以及验证 token。
package com.example.demo;public class jwttokenutil implements serializable { private string secret; private long expiration; private string header; private string generatetoken(map claims) { date expirationdate = new date(system.currenttimemillis() + expiration); return jwts.builder().setclaims(claims).setexpiration(expirationdate).signwith(signaturealgorithm.hs512, secret).compact(); } private claims getclaimsfromtoken(string token) { claims claims; try { claims = jwts.parser().setsigningkey(secret).parseclaimsjws(token).getbody(); } catch (exception e) { claims = null; } return claims; } public string generatetoken(userdetails userdetails) { map claims = new hashmap(2); claims.put(sub, userdetails.getusername()); claims.put(created, new date()); return generatetoken(claims); } public string getusernamefromtoken(string token) { string username; try { claims claims = getclaimsfromtoken(token); username = claims.getsubject(); } catch (exception e) { username = null; } return username; } public boolean istokenexpired(string token) { try { claims claims = getclaimsfromtoken(token); date expiration = claims.getexpiration(); return expiration.before(new date()); } catch (exception e) { return false; } } public string refreshtoken(string token) { string refreshedtoken; try { claims claims = getclaimsfromtoken(token); claims.put(created, new date()); refreshedtoken = generatetoken(claims); } catch (exception e) { refreshedtoken = null; } return refreshedtoken; } public boolean validatetoken(string token, userdetails userdetails) { jwtuser user = (jwtuser) userdetails; string username = getusernamefromtoken(token); return (username.equals(user.getusername()) && !istokenexpired(token)); }} 编写拦截器 编写 filter 用来检测 jwt
package com.example.demo;@componentpublic class jwtauthenticationtokenfilter extends onceperrequestfilter { @autowired private userdetailsservice userdetailsservice; @autowired private jwttokenutil jwttokenutil; @override protected void dofilterinternal(httpservletrequest httpservletrequest, httpservletresponse httpservletresponse, filterchain filterchain) throws servletexception, ioexception { string authheader = httpservletrequest.getheader(jwttokenutil.getheader()); if (authheader != null && stringutils.isnotempty(authheader)) { string username = jwttokenutil.getusernamefromtoken(authheader); if (username != null && securitycontextholder.getcontext().getauthentication() == null) { userdetails userdetails = this.userdetailsservice.loaduserbyusername(username); if (jwttokenutil.validatetoken(authheader, userdetails)) { usernamepasswordauthenticationtoken authentication = new usernamepasswordauthenticationtoken(userdetails,null,userdetails.getauthorities()); authentication.setdetails(new webauthenticationdetailssource().builddetails(httpservletrequest)); securitycontextholder.getcontext().setauthentication(authentication); } } } filterchain.dofilter(httpservletrequest, httpservletresponse); }} 编写 userdetailsservice 的实现类 在上方代码中,编写 userdetailsservice,类,实现其验证过程
package com.example.demo;@servicepublic class jwtuserdetailsserviceimpl implements userdetailsservice { @autowired private usermapper usermapper; @override public userdetails loaduserbyusername(string s) throws usernamenotfoundexception { user user = usermapper.selectbyusername(s); if (user == null) { throw new usernamenotfoundexception(string.format('%s'.这个用户不存在, s)); } list collect = user.getroles().stream().map(role::new).collect(collectors.tolist()); return new jwtuser(user.getusername(), user.getpassword(), user.getstate(), collect); }} 编写登录 编写登录业务的实现类 其 login 方法会返回一个 jwtutils 的 token
@servicepublic class userserviceimpl implements userservice { @autowired private usermapper usermapper; @autowired private authenticationmanager authenticationmanager; @autowired private userdetailsservice userdetailsservice; @autowired private jwttokenutil jwttokenutil; public user findbyusername(string username) { user user = usermapper.selectbyusername(username); return user; } public retresult login(string username, string password) throws authenticationexception { usernamepasswordauthenticationtoken uptoken = new usernamepasswordauthenticationtoken(username, password); final authentication authentication = authenticationmanager.authenticate(uptoken); securitycontextholder.getcontext().setauthentication(authentication); userdetails userdetails = userdetailsservice.loaduserbyusername(username); return new retresult(retcode.success.getcode(),jwttokenutil.generatetoken(userdetails)); }} 最后配置 config @enableglobalmethodsecurity(prepostenabled = true)@enablewebsecuritypublic class websecurity extends websecurityconfigureradapter { @autowired private userdetailsservice userdetailsservice; @autowired private jwtauthenticationtokenfilter jwtauthenticationtokenfilter; @autowired public void configureauthentication(authenticationmanagerbuilder authenticationmanagerbuilder) throws exception { authenticationmanagerbuilder.userdetailsservice(this.userdetailsservice).passwordencoder(passwordencoder()); } @bean(name = beanids.authentication_manager) @override public authenticationmanager authenticationmanagerbean() throws exception { return super.authenticationmanagerbean(); } @bean public passwordencoder passwordencoder() { return new bcryptpasswordencoder(); } @override protected void configure(httpsecurity http) throws exception { http.csrf().disable().sessionmanagement().sessioncreationpolicy(sessioncreationpolicy.stateless) .and().authorizerequests() .antmatchers(httpmethod.options, /**).permitall() .antmatchers(/auth/**).permitall() .anyrequest().authenticated() .and().headers().cachecontrol(); http.addfilterbefore(jwtauthenticationtokenfilter, usernamepasswordauthenticationfilter.class); expressionurlauthorizationconfigurer.expressionintercepturlregistry registry = http.authorizerequests(); registry.requestmatchers(corsutils::ispreflightrequest).permitall(); } @bean public corsfilter corsfilter() { final urlbasedcorsconfigurationsource urlbasedcorsconfigurationsource = new urlbasedcorsconfigurationsource(); final corsconfiguration cors = new corsconfiguration(); cors.setallowcredentials(true); cors.addallowedorigin(*); cors.addallowedheader(*); cors.addallowedmethod(*); urlbasedcorsconfigurationsource.registercorsconfiguration(/**, cors); return new corsfilter(urlbasedcorsconfigurationsource); }} 运行,返回 token 运行,返回结果为 token
这里配置 springsecurity 之 json 登录
这里需要重写 usernamepasswordanthenticationfilter 类,以及配置 springsecurity
重写 usernamepasswordanthenticationfilter 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()){ authenticationbean authenticationbean = mapper.readvalue(is,authenticationbean.class); authrequest = new usernamepasswordauthenticationtoken( authenticationbean.getusername(), authenticationbean.getpassword()); }catch (ioexception e) { e.printstacktrace(); authrequest = new usernamepasswordauthenticationtoken( , ); }finally { setdetails(request, authrequest); return this.getauthenticationmanager().authenticate(authrequest); } } else { return super.attemptauthentication(request, response); } }} 配置 securityconfig @overrideprotected void configure(httpsecurity http) throws exception { http .cors().and() .antmatcher(/**).authorizerequests() .antmatchers(/, /login**).permitall() .anyrequest().authenticated() .and().formlogin().loginpage(/) .and().csrf().disable(); http.addfilterat(customauthenticationfilter(), usernamepasswordauthenticationfilter.class);}@beancustomauthenticationfilter customauthenticationfilter() throws exception { customauthenticationfilter filter = new customauthenticationfilter(); filter.setauthenticationsuccesshandler(new successhandler()); filter.setauthenticationfailurehandler(new failurehandler()); filter.setfilterprocessesurl(/login/self); filter.setauthenticationmanager(authenticationmanagerbean()); return filter;} 这样就完成使用 json 登录 springsecurity
需要在 config 类中配置如下内容
@bean public bcryptpasswordencoder passwordencoder(){ return new bcryptpasswordencoder(); } 即,使用此方法,对密码进行加密, 在业务层的时候,使用此加密的方法
@service@transactionalpublic class userserviceimpl implements userservice { @resource private userrepository userrepository; @resource private bcryptpasswordencoder bcryptpasswordencoder; @override public user add(user user) { user.setpassword(bcryptpasswordencoder.encode(user.getpassword())); user user2 = userrepository.save(user); return user2; } @override public resultinfo login(user user) { resultinfo resultinfo=new resultinfo(); user user2 = userrepository.findbyname(user.getname()); if (user2==null) { resultinfo.setcode(-1); resultinfo.setmessage(用户名不存在); return resultinfo; } if (!bcryptpasswordencoder.matches(user.getpassword(),user2.getpassword())) { resultinfo.setcode(-1); resultinfo.setmessage(密码不正确); return resultinfo; } resultinfo.setmessage(登录成功); return resultinfo; }} 即,使用 bcryptpasswordencoder 对密码进行加密,保存数据库
这里使用数据库认证 springsecurity
设计数据表 这里设计数据表
着重配置 springconfig @configurablepublic class websecurityconfig extends websecurityconfigureradapter { @autowired private userservice userservice; @bean passwordencoder passwordencoder(){ return new bcryptpasswordencoder(); } @override protected void configure(authenticationmanagerbuilder auth) throws exception { auth.userdetailsservice(userservice); } @override protected void configure(httpsecurity http) throws exception { http.authorizerequests() .antmatchers(/admin/**).hasrole(admin) .anyrequest().authenticated() .and() .formlogin() .loginprocessingurl(/login).permitall() .and() .csrf().disable(); }} 这里着重配置 springconfig。
着重讲解了 rbac 的权限配置,以及简单的使用 springsecurity,以及使用 springsecurity + jwt 完成前后端的分离,以及配置 json 登录,和密码加密方式。
现在入手iPhone11还是iPhone12?
卢伟冰发表Redmi品牌独立宣言 死磕品质追求极致性价比
科瑞技术最新升级的切叠一体机首次亮相
华润微:在手订单饱满,多个业务板块齐头并进
高通加速布局VR/无人机 物联网时代能否复制辉煌
你知道怎么Spring security整合JWT么
一款十分强大的文件加密解密CLI工具toplip的详细资料概述
简要概述LoRa技术在智慧工厂的应用
为什么多数人会选择购买贴片电阻
万用表9V电池代换电路,1.5 to 9v dc-dc converter
最具有性价比降噪蓝牙耳机,学生党最值得推荐的耳机排名
浅析用几何描绘物理学的统一理论
柏科数据与宁算科技,联合建设“西部高原数据湖”
数字化转型的主要方向是什么
限幅电路的工作原理
思科宣布将2.71亿美元现金收购Lightwire
重磅!英国:华为确实存在风险!
马斯克:特斯拉的目标是最终达到每年生产2000万辆电动汽车
电力脱硫湿式球磨机主轴磨损了?这种免拆卸的修复方法用过的都说好
电气工程施工阶段的质量控制措施