b2b2c系统jwt权限源码分享part2
程序员文章站
2022-03-10 12:39:31
在上一篇《b2b2c系统jwt权限源码分享part1》中和大家分享了b2b2c系统中jwt权限的基础设计及源码,本文继续和大家分享jwt和spring security整合部分的思路和源码。 在上一篇文章中已经分享了关键的类图: 如上图所示,权限的校验主要涉及到四个类: AbstractAuthen ......
在上一篇《》中和大家分享了b2b2c系统中jwt权限的基础设计及源码,本文继续和大家分享jwt和spring security整合部分的思路和源码。
在上一篇文章中已经分享了关键的类图:
如上图所示,权限的校验主要涉及到四个类:
-
abstractauthenticationservice
-
buyerauthenticationservice
-
sellerauthenticationservice
-
adminauthenticationservice
abstractauthenticationservice
对于三端(买家买家管理端)验权的公用部分我们抽象在abstractauthenticationservice中:
public abstract class abstractauthenticationservice implements authenticationservice { @autowired protected tokenmanager tokenmanager; private final logger logger = loggerfactory.getlogger(getclass()); /** * 单例模式的cache */ private static cache<string, integer> cache; @autowired private javashopconfig javashopconfig; /** * 鉴权,先获取token,再根据token来鉴权 * 生产环境要由nonce和时间戳,签名来获取token * 开发环境可以直接传token * * @param req */ @override public void auth(httpservletrequest req) { string token = this.gettoken(req); if (stringutil.notempty(token)) { authentication authentication = getauthentication(token); if (authentication != null) { securitycontextholder.getcontext().setauthentication(authentication); } } } /** * 接收用户禁用或解禁事件<br/> * 禁用:将被禁用的用户id写入缓存 * 解禁:将缓存中存放的用户id删除 * * @param userdisablemsg */ @override public void userdisableevent(userdisablemsg userdisablemsg) { //在缓存中记录用户被禁用 cache<string, integer> cache = this.getcache(); if (userdisablemsg.add.equals(userdisablemsg.getoperation())) { logger.debug("收到用户禁用消息:" + userdisablemsg); cache.put(getkey(userdisablemsg.getrole(), userdisablemsg.getuid()), 1); } if (userdisablemsg.delete.equals(userdisablemsg.getoperation())) { logger.debug("收到用户解禁消息:" + userdisablemsg); cache.remove(getkey(userdisablemsg.getrole(), userdisablemsg.getuid()), 1); } } protected void checkuserdisable(role role, int uid) { cache<string, integer> cache = this.getcache(); integer isdisable = cache.get(getkey(role, uid)); if (isdisable == null) { return; } if (1 == isdisable) { throw new runtimeexception("用户已经被禁用"); } } private string getkey(role role, int uid) { return role.name() + "_" + uid; } /** * 解析一个token * 子类需要将token解析自己的子业务权限模型:admin,seller buyer... * * @param token * @return */ protected abstract authuser parsetoken(string token); /** * 根据一个 token 生成授权 * * @param token * @return 授权 */ protected authentication getauthentication(string token) { try { authuser user = parsetoken(token); list<grantedauthority> auths = new arraylist<>(); list<string> roles = user.getroles(); for (string role : roles) { auths.add(new simplegrantedauthority("role_" + role)); } usernamepasswordauthenticationtoken authentication = new usernamepasswordauthenticationtoken("user", null, auths); authentication.setdetails(user); return authentication; } catch (exception e) { logger.error("认证异常", e); return new usernamepasswordauthenticationtoken("anonymous", null); } } /** * 获取token * 7.2.0起,废弃掉重放攻击的判断 * * @param req * @return */ protected string gettoken(httpservletrequest req) { string token = req.getheader(tokenconstant.header_string); if (stringutil.notempty(token)) { token = token.replaceall(tokenconstant.token_prefix, "").trim(); } return token; } private static final object lock = new object(); /** * 获取本地缓存<br/> * 用于记录被禁用的用户<br/> * 此缓存的key为:角色+用户id,如: admin_1 * value为:1则代表此用户被禁用 * * @return */ protected cache<string, integer> getcache() { if (cache != null) { return cache; } synchronized (lock) { if (cache != null) { return cache; } //缓存时间为session有效期+一分钟 //也就表示,用户如果被禁用,session超时这个cache也就不需要了: //因为他需要重新登录就可以被检测出无效 int sessiontimeout = javashopconfig.getrefreshtokentimeout() - javashopconfig.getaccesstokentimeout() + 60; //使用ehcache作为缓存 cachingprovider provider = caching.getcachingprovider("org.ehcache.jsr107.ehcachecachingprovider"); cachemanager cachemanager = provider.getcachemanager(); mutableconfiguration<string, integer> configuration = new mutableconfiguration<string, integer>() .settypes(string.class, integer.class) .setstorebyvalue(false) .setexpirypolicyfactory(createdexpirypolicy.factoryof(new duration(timeunit.seconds, sessiontimeout))); cache = cachemanager.createcache("userdisable", configuration); return cache; } } }
在 禁用用户要求该用户立刻无法操作,这部分功能体现在
checkuserdisable方法中,思路是通过监听redis消息将禁用用户放在本地cache中(这里采用的事ehcache。
buyerauthenticationservice
有了之前的代码基础,三端的权限校验就比较简单了:
@component public class buyerauthenticationservice extends abstractauthenticationservice { @override protected authuser parsetoken(string token) { authuser authuser= tokenmanager.parse(buyer.class, token); user user = (user) authuser; checkuserdisable(role.buyer, user.getuid()); return authuser; } }
sellerauthenticationservice
@component public class sellerauthenticationservice extends abstractauthenticationservice { /** * 将token解析为clerk * * @param token * @return */ @override protected authuser parsetoken(string token) { authuser authuser = tokenmanager.parse(clerk.class, token); user user = (user) authuser; checkuserdisable(role.clerk, user.getuid()); return authuser; } }
adminauthenticationservice
@component public class adminauthenticationservice extends abstractauthenticationservice { /** * 将token解析为admin * @param token * @return */ @override protected authuser parsetoken(string token) { authuser authuser= tokenmanager.parse(admin.class, token); user user = (user) authuser; checkuserdisable(role.admin, user.getuid()); return authuser; } }
整合security:
@configuration @enablewebsecurity public class buyersecurityconfig extends websecurityconfigureradapter { @autowired private domainhelper domainhelper; @autowired private buyerauthenticationservice buyerauthenticationservice; @autowired private accessdeniedhandler accessdeniedhandler; @autowired private authenticationentrypoint authenticationentrypoint; /** * 定义seller工程的权限 * * @param http * @throws exception */ @override public void configure(httpsecurity http) throws exception { http.cors().configurationsource((corsconfigurationsource) applicationcontextholder.getbean("corsconfigurationsource")).and().csrf().disable() //禁用session .sessionmanagement().sessioncreationpolicy(sessioncreationpolicy.stateless).and() //定义验权失败返回格式 .exceptionhandling().accessdeniedhandler(accessdeniedhandler).authenticationentrypoint(authenticationentrypoint).and() .authorizerequests() .and() .addfilterbefore(new tokenauthenticationfilter(buyerauthenticationservice), usernamepasswordauthenticationfilter.class); //过滤掉swagger的路径 http.authorizerequests().antmatchers("/v2/api-docs", "/configuration/ui", "/swagger-resources", "/configuration/security", "/swagger-ui.html", "/webjars/**").anonymous(); //过滤掉不需要买家权限的api http.authorizerequests().antmatchers("/debugger/**" ).permitall().and(); //定义有买家权限才可以访问 http.authorizerequests().anyrequest().hasrole(role.buyer.name()); http.headers().addheaderwriter(xframeoptionsheaderwriter()); //禁用缓存 http.headers().cachecontrol().and() .contentsecuritypolicy("script-src 'self' 'unsafe-inline' ; frame-ancestors " + domainhelper.getbuyerdomain()); }
以上就是中关于权限相关的分享。
上一篇: Composer 设置为全局
下一篇: 如何设计一个安全的对外接口