• b2b2c系统jwt权限源码分享part2


      在上一篇《b2b2c系统jwt权限源码分享part1》中和大家分享了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;
            }
        }
    
    }
     
    
     

    javashop b2b2c系统中 禁用用户要求该用户立刻无法操作,这部分功能体现在

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

     

    以上就是javashop电商系统源码中关于权限相关的分享。

     

     

     

  • 相关阅读:
    ICS SIP Call移植
    ubuntu常用软件安装
    ubuntu开机自动设置屏幕亮度
    书摘《苹果是方的》
    dbml 添加时自动生成 Guid & DataTime
    English 中有趣的a和d
    asp.net 生成ul控件
    lambda c# 3.0
    0809 END Lakers
    linq c# 3.0
  • 原文地址:https://www.cnblogs.com/javashop-docs/p/12661945.html
Copyright © 2020-2023  润新知