当前位置 博文首页 > chuanyuan8669的博客:基于springboot+mybatisplus构建系统管理

    chuanyuan8669的博客:基于springboot+mybatisplus构建系统管理

    作者:[db:作者] 时间:2021-07-27 14:53

    此次用到的技术当然以spring为核心,orm选择目前国产开源框架mybatisplus,由于没有对该框架有非常深度的认识,具体怎么样目前不做评论,安全框架使用springsecurity,由于之前对该框架一直没有了解太多,借此机会看是否可能更深度的理解其工作原理与设计理念,前端准备尝试使用vue,还是一样,没怎么用过,但是总要尝试一下。由于涉及到的技术点相对比较生疏,因此此次过程会相对艰辛与困难,但是,拭目以待!

    首先在springboot中使用springsecurity比较简单,引入对应的依赖jar

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    其实就可以使用了,只不过这个时候我们在发送请求时,会统一被拦截打开一个飙到,需要输入用户名,密码才能继续下面的操作,而默认的用户为user,密码在系统启动时控制台中

    5dd862fb3e274488120f3dda228eaf1e719.jpg

    当然,我们真正去使用时,不可能用这些默认配置,一般都会根据自己的业务需要添加一些认证逻辑与权限控制相关的逻辑。

    那么扩展从使用角度来不复杂,但是如果去理解其原理或者配置比较复杂的权限模型,很不容易。首先我们需要自定义认证配置类,比如:

    @NoArgsConstructor
    @Configuration
    @EnableWebSecurity
    @EnableConfigurationProperties(WebSecurityProperties.class)
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter {}

    关键点,添加@EnableWebSecurity,继承WebSecurityConfigurerAdapter。

    基本所有的业务都是在该类中进行扩展,维护,当然默认的配置已经帮助我们实现了基础功能,我只用根据需要重写对应的方法,这里说几个关键方法:

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        
    }

    该方法主要传入对象auth,主要涉及到用户认证的逻辑,比如构建用户认证实体,和shiro一样,在shiro中是通过Subject,而这里则是UserDetails,那么我们可以怎么配置:

    *  1、基于内存 auth.inMemoryAuthentication().withUser("user").password("password");
    *  2、基于指定数据源 auth.jdbcAuthentication().usersByUsernameQuery("select username,password,enabled from users where username = ?");
    *  3、基于名录库 auth.ldapAuthentication()...
    *  4、基于 UserDetailsService服务,需要自己实现该接口
    *  5、基于 AuthenticationProvider,默认情况下authentication = UsernamePasswordAuthenticationToken,这个可以在我们的UserDetailsService中定义,
    *  AuthenticationProvider有多种实现

    站在开发者角度,我们一般通过两种方式进行用户认证,其实原理都是通过AuthenticationProvider实现,只不过我们开发的时候关注点在哪:

    1、通过构建内置的UserDetailsAwareConfigurer,进而获取UserDetailsService,最后来构建UserDetails

    2、或者通过配置AuthenticationProvider实现多类型用户登录

    最简单的实现方式,由于我们一般系统用户体系都依赖于数据库,可以这样配置

    auth.userDetailsService(userDetailsService)

    其中userDetailsService也只用去实现UserDetailsService接口,在该接口中,我们能够拿到页面中输入的用户名,这里只需要根据用户名查询出用户,并构建UserDetails返回即可

    @Service
    public class DefaultUserDetailesService implements UserDetailsService {
        @Autowired
        private UserHolderService userHolderService;
    
        @Override
        public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
            User user = userHolderService.getUser(username);
            if(user==null) return null;
    ????????return new User(user.getUsername(),user.getPassword(),null);
        }
    }

    然后看下第二个需要重写的,我们知道,在mvc中对static、publc、resources等资源可以直接访问,但是假如security后,会被拦截,所以需要单独处理下。

    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/static/**").antMatchers("/favicon.ico")
                .mvcMatchers("/webjars/**");
    }

    最后一个,也是比较复杂的,下面有一段简单的配置

    protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
        .antMatchers("/login").permitAll()
        .anyRequest().authenticated()
        .and().formLogin().loginPage("/login").defaultSuccessUrl("/index").loginProcessingUrl("/login")
        .and().logout().permitAll()
        .and().csrf().disable();
    }

    其中有几点需要注意的是,我们设置了登录界面的请求为/login,由于spring默认的登录请求也是这个,可以在DefaultLoginPageGeneratingFilter中看到,那么会出现什么问题呢,在最开始使用该框架时,我们不用任何配置,访问接口时会自动进入一个需要输入用户名、密码的界面,就是因为请求默认进入这个拦截器中了,如果我们将自己的请求页面也这样配置,那么将不会进入我们自己的页面,所以可以将我们的登录请求该一下。当然,如果你坚持也要用/login,那么需要做些处理了。

    在WebSecurityConfigurerAdapter构造器中,我们可以看到有一个参数disableDefaults,默认情况为false,那么就会加载一些系统默认的配置,我们可以将其改为true,那么DefaultLoginPageGeneratingFilter将不再生效,也就可以进入我们自己的页面中进行登录了。当然,这样又会出现另一个问题,An Authentication object was not found in the SecurityContext 要知道我们去掉了默认的配置,是否弄清了默认有哪些配置,如果把一些必要的元素取消了,那是否需要有对应的配置去代替?如果不知道,则还是保持原样把。

    9c19a61c7765006cae0d8840063e5ccb006.jpg

    还有一个配置loginProcessingUrl,这个是什么意思?其实是在我们进行form提交时的action,必须要与我们配置的一致,并且为POST类型请求,否则,你提交请求时怎么进行用户认证?当然你不配置也行,因为默认使用的是loginPage属性。如果想深入了解,可以看UsernamePasswordAuthenticationFilter

    public UsernamePasswordAuthenticationFilter() {
       super(new AntPathRequestMatcher("/login", "POST"));
    }

    还有几点需要说明的是,正常form表单提交,如果认证成功,会自动跳转到我们配置的成功页面,但是有时候我们使用的是ajax提交,可能就不能用默认的处理方式了,这个时候不放配置一下

    http.formLogin().successHandler(new AuthenticationSuccessHandler() {
        @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
    
        }
    });

    如果我们不配置,默认也会使用一个,SavedRequestAwareAuthenticationSuccessHandler。

    其实spring security本身是一个功能强大内容繁琐的框架,当然如果站在使用者的角度其实没那么复杂,但是如果想了解其完整的技术实现细节可能不是三言两语可以概括,所以目前也不再过多深入。

    下面说下mybatis-plus,用过mybatis的都知道,mybatis作为一个相对简单的半ORM框架,因为只实现了数据->实体的映射,其主要为我们封装了jdbc的一些基础功能,比如连接的创建、事务的管理、返回值的封装等等,但是具体如何实现jdbc的功能,其实都是需要我们自己去实现的,最终将这块模块统一为xml包装的sql,所以说其灵活在与由开发者自己去完成想要的功能了。而mybatis-plus,没有对原框架做任何的修改,只不过在其基础上 进行了一定的扩展,就之前使用的情况来看,主要有以下几个方面:

    1、构建了统一的mapper实现,实现了单表的增删改查

    2、定义了基础的service,供开发者使用

    3、添加 了id生存策略

    4、添加了分页和一些其它的拦截器

    5、增加了lambda表达式,快速构建CRUD操作条件

    6、添加了代码生成模板,快速通过表生成完整的代码

    由于之前使用的不是太深入,所有也有很多功能没有用到,只是做了简单的罗列,而我们使用起来其实也没那么复杂,首先需要引入相关的依赖jar:

    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>${mybatisplus.version}</version>
    </dependency>

    其次在配置文件中根据自己的情况加入下面的配置:

    mybatis-plus.mapper-locations=classpath*:com/sucl/smsm/*/mapper/**Mapper.xml
    #实体扫描,多个package用逗号或者分号分隔
    mybatis-plus.type-aliases-package=com.sucl.smsm.*.entity
    mybatis-plus.global-config.refresh=true
    mybatis-plus.global-config.db-config.db-type: mysql
    #      id-type: uuid
    mybatis-plus.configuration.map-underscore-to-camel-case=true
    mybatis-plus.configuration.cache-enabled=false

    之后就是一些entity、mapper、service、web的写法了

    @Data
    @EqualsAndHashCode(callSuper = true)
    //@Accessors(chain = true)
    public class User extends BaseEntity implements IUser {
    
        private static final long serialVersionUID = 1L;
    
        @TableId(value = "USER_ID", type = IdType.UUID)
        private String userId;
    
        @TableField("USER_NAME")
        private String username;
    }
    public interface UserMapper extends BaseMapper<User> {
    
    }
    public interface UserService extends IService<User> {
    
    }
    @Service
    @Transactional
    public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService{
    
    }
    @RestController
    @RequestMapping("/sys/user")
    public class UserController extends BaseController<UserService,User> {
    
    }

    可以看到,我们没有定义任何的方法,但是却有了基本的功能能够 完成单表几乎任何的操作,这主要归根与BaseMapper、IService、ServiceImpl这些写内置的接口与实现。

    因此对于后台的开发相对比较简单了。

    当然这只是完成了其中一部分,说好的vue还没用到,上面说的这些究竟如何实现,这些会在之后进行完善,毕竟篇幅较大,不利于阅读。

    代码:https://github.com/suspring/springboot-mybatisplus-security-ms.git

    代码持续更新中...

    cs