使用spring实现的一个电商系统

该项目基于 SpringCloud 搭建的一个电商系统,采用前后端分离微服务架构,实现了基于 RBAC/JWT 权限认证的解决方案,集成了各种微服务治理和监控功能。模块包括:业务逻辑层管理、 管理员角色权限系统、应用监控、Druid 数据库监控、日志系统(ELK)、配置中心、Swagger 文档、任务调度等。

用户端目前只做了小程序,安卓、IOS端APP可以直接使用接口来开发。

架构图如下:

 

小程序端

Nacos配置中心

Grafana监控系统

Kibana日志系统

Druid监控系统

Swagger文档

系统模块

leaf-admin:总后台管理系统

leaf-business:商家管理系统

leaf-client:用户端,小程序、app的使用接口

leaf-common:公共模块系统,里面放了一些公告模块和公共类比如:swagger、lombok、分页工具、公共返回结果类、错误代码、公共异常处理等

leaf-gateway:网关

leaf-mbg:使用mybatis和generator自动生成的Entity实体和mapper数据库访问层

leaf-order:订单服务

leaf-score:积分服务

leaf-security:使用spring security封装的一个验证、鉴权模块,实现了使用jwt验证的和RBAC基于角色的访问控制功能

使用 Security 结合 JWT 实现 url 级的权限控制系统,使用 Generator/Mybatis 自动生成数据库操作 的基本方法,数据库连接池使用 Druid 配置多个数据源,并实现了读写分离。日志系统通过 Logback 写到 Redis 队列中供 Logstash 读取,使用 Prometheus、Grafana 对 Redis、Mysql 和微服务进行监控。使用 Redis 实现了分布式锁和分布式事务(TX-LCN/Seata/RocketMQ), 使用 Sentinel 实现服务降级、熔断、限流和监控。

一、leaf-common模块

pom文件依赖了swagger2、lombok这些公共模块,不用重复依赖。

CommonPage类:分页的公共处理对象,封装了pagehelper的总页数、当前页等信息

CommonResult类:公共的结果返回对象,定义了返回的code、message、data字段和封装了一些常用的静态返回方法,比如在控制器中直接使用return CommonResult.success(data);返回结果。

ApiException:定义了我们自己的异常处理类,继承自RuntimeException类,然后创建一个类GlobalExceptionHandler添加全局异常处理类使用注解@GlobalExceptionHandler代码如下:

@ControllerAdvice
public class GlobalExceptionHandler {

    @ResponseBody
    @ExceptionHandler(value = ApiException.class)
    public CommonResult handle(ApiException e) {
        if (e.getErrorCode() != null) {
            return CommonResult.failed(e.getErrorCode());
        }
        return CommonResult.failed(e.getMessage());
    }
}

这样只要在项目中抛出ApiException异常都会被handle捕获,返回通用的CommonResult结果。

还定义了断言类Asserts、返回错误码ResultCode等。

二、leaf-security

这个模块使用spring security封装的一个验证、鉴权的功能,它使用了spring security库。关于security我之前写过一篇介绍原理的博客分析Spring Security实现方式,在这里我只说一下它的应用,在日常的用户登录有以下场景:

一、用户发送username和password到服务器,控制器调用service层去数据库进行账号密码的验证;

二、如果验证成功,把用户信息封装成一个UserDetails使用jwt工具类生成一个token返还给客户端,类似这样eyJhbGciOiJIUzUxMiJ9.eyJuYW1lIjoi6a2P5Lqa5oGSIiwiZXhwIjoxNTg5Nzc2ODMzLCJhZ2UiOjEyM30.J0WvlpSvuS0OIyEpS4uEMqqO2PCWWkWLkFmQKX2l4vl-vVNTpdbiTcNTEj8qX3EFBZUYOIBLfgacaKMH4dCmAQ红色部分是JWT的头部,它定义这个token的加密算法类型,之后使用base64进行编码。黄色部分记录了信息比如用户名、过期时间等然后进行base64编码,最后绿色是对前面两部分内容进行sha256算法签名,秘钥在服务器可以有效防止数据被篡改。关于JWT的介绍可以看这篇文章JSON Web Token

三、用户端这时候拿到token可以放到cookie中也可以放到本地存储器中,在访问服务器时把它放在HTTP的Header头中。

四、项目使用了security,所以请求到达控制器之前会先通过UsernamePasswordAuthenticationFilter过滤器,我们在UsernamePasswordAuthenticationFilter过滤器之前添加一个自己的过滤器jwtAuthenticationTokenFilter,在这个过滤器中我们验证这个JWT的签名是否合法,并且拿出里面的username,如果这个username在数据库中存在(因为操作数据库频率高,所以使用redis做了缓存),我们拿到用户UserDetails,然后创建一个UsernamePasswordAuthenticationToken实例authentication,再把authentication放到SecurityContextHolder中,这时候security就会从SecurityContext中拿到这个有效的authentication进行验证了。

五、之后就是角色鉴权的流程了,本文主要介绍系统的整体架构,由于篇幅原因关于角色鉴权具体实现可以看我之前的文章分析Spring Security实现方式进行了解。

这里贴一下重要类的实现及配置,关于SecurityConfig的配置类,代码如下:

public class SecurityConfig extends WebSecurityConfigurerAdapter {


    @Autowired(required = false)
    private DynamicSecurityService dynamicSecurityService;

    @Override
    protected void configure(HttpSecurity httpSecurity) throws Exception {
        // authorizeRequests方法 定义哪些URL需要被保护、哪些不需要被保护
        ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = httpSecurity
                .authorizeRequests();
        //不需要保护的资源路径允许访问
        for (String url : ignoreUrlsConfig().getUrls()) {
            registry.antMatchers(url).permitAll();
        }
        //允许跨域请求的OPTIONS请求
        registry.antMatchers(HttpMethod.OPTIONS)
                .permitAll();
        // 任何请求需要身份认证
        registry.and()
                .authorizeRequests()
                .anyRequest()
                .authenticated()
                // 关闭跨站请求防护及不使用session
                .and()
                .csrf()
                .disable()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                // 自定义权限拒绝处理类 添加自定义未授权和未登录结果返回
                .and()
                .exceptionHandling()
                .accessDeniedHandler(restfulAccessDeniedHandler())
                .authenticationEntryPoint(restAuthenticationEntryPoint())
                // 自定义权限拦截器JWT过滤器
                .and()
                .addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
        //有动态权限配置时添加动态权限校验过滤器
        if(dynamicSecurityService!=null){
            registry.and().addFilterBefore(dynamicSecurityFilter(), FilterSecurityInterceptor.class);
        }
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
   
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值