b2b2c system jwt permission source code sharing part2

  In the previous article "b2b2c system jwt authority source code sharing part1", I shared with you the basic design and source code of jwt authority in b2b2c system. This article continues to share with you the ideas and source code of jwt and spring security integration.

The key class diagrams have been shared in the previous article:

As shown in the figure above, the verification of permissions mainly involves four categories:

  • AbstractAuthenticationService
  • BuyerAuthenticationService
  • SellerAuthenticationService
  • AdminAuthenticationService

AbstractAuthenticationService


For the common part of the three-terminal (buyer buyer management end) authentication, we abstract it in 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;
        }
    }

}

Disabling a user in the javashop b2b2c system requires that the user cannot operate immediately. This part of the function is reflected in

In the checkUserDisable method, the idea is to place the disabled user in the local cache by listening to the redis message (the EHCache is used here.

BuyerAuthenticationService


With the previous code base, the three-terminal permission verification is relatively simple:

@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;

    }

}

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

    }

The above is the sharing of permissions in the source code of javashop e-commerce system .

Original article by javashop

{{o.name}}
{{m.name}}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324232727&siteId=291194637