欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

b2b2c系统jwt权限源码分享part2

程序员文章站 2022-03-10 12:39:31
在上一篇《b2b2c系统jwt权限源码分享part1》中和大家分享了b2b2c系统中jwt权限的基础设计及源码,本文继续和大家分享jwt和spring security整合部分的思路和源码。 在上一篇文章中已经分享了关键的类图: 如上图所示,权限的校验主要涉及到四个类: AbstractAuthen ......

  在上一篇《》中和大家分享了b2b2c系统中jwt权限的基础设计及源码,本文继续和大家分享jwt和spring security整合部分的思路和源码。

在上一篇文章中已经分享了关键的类图:

b2b2c系统jwt权限源码分享part2

 

如上图所示,权限的校验主要涉及到四个类:

  • 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());

    }

 

 

以上就是中关于权限相关的分享。