人工智能的网站,python 可以做网站吗,赣州港招聘信息,沈阳网页设计培训学校Hi I’m Shendi 快速开发框架若依的基础使用详解 最近在为公司制作新的项目#xff0c;经过了一段时间的技术沉淀#xff0c;我开始尝试接触市面上用的比较多的快速开发框架#xff0c;听的最多的当属若依吧
于是就选用了若依 介绍
为什么选#xff1f;目的是为了提高开发…Hi I’m Shendi 快速开发框架若依的基础使用详解 最近在为公司制作新的项目经过了一段时间的技术沉淀我开始尝试接触市面上用的比较多的快速开发框架听的最多的当属若依吧
于是就选用了若依 介绍
为什么选目的是为了提高开发效率至于能不能提高得实践才能知晓。 若依的官网http://www.ruoyi.vip/
是一个快速开发框架典型的一个后台管理系统。 RuoYi-Vue 是一个 Java EE 企业级快速开发平台基于经典技术组合Spring Boot、Spring Security、MyBatis、Jwt、Vue内置模块如部门管理、角色用户、菜单及按钮授权、数据权限、系统参数、日志管理、代码生成等。在线定时任务配置支持集群支持多数据源支持分布式事务。 可以通过官网的在线演示看到效果当然没有具体使用并没有这方面经验的话看这些大概都是云里雾里的
若依分几个版本但都是基于SpringBoot的这点没有问题因为SpringBoot是目前Java用的最多的JavaWeb框架了 我目前挑选的是前后端分离版本若依除此之外还有
微服务版公司有一定规模了有很多台服务器的话可以用不分离版现在基本上都前后端分离了不分离的基本上没人用了吧 从侧边栏讲起先大概有一个了解 首先若依表面上的有三个大部分
系统管理系统监控系统工具 其中系统管理是最核心的部分包含了用户角色菜单部门…可以说是一个ERP系统了
而这些都是已经做好了的我们使用的话就不需要自己写登录做权限菜单这些了需要根据自己的业务去更改 若依是使用了目前市面上流行的一些开源框架。Spring系列比如做权限验证前后端分离版是用的Spring Security…
我使用了的这段时间给我的感觉就是”还行“对我来说重要的部分就是将权限都做好了我只需要专心写接口就可以了。大体我也讲不上来所以写下这篇文章来总结一下。 架构
使用了 JavaMysqlRedis
具体参考官方文档 项目的下载与导入
我选的前后端分离版前端使用的vue系列
首先从git上拉项目。或者下载压缩包也行在官网上进入对应的仓库地址 我这里直接使用 Git 下载了因为这样比较快至于没有 Git 的先要去下载 Git
打开 Git bash进入到自己新建的文件夹中使用 git clone 命令从网络上将git项目克隆下来
git clone https://gitee.com/y_project/RuoYi-Vue.gitps: 至于 git 的地址在官方源码的Gitee中点击克隆就可以获取到了 下载下来的目录结构和仓库展示的目录结构是差不多的 有的时候我们会给文件夹改名称但是建议如果对这些不是很熟练的话最好先不要改。不然就会和我一样花一些时间去补错误了 其中ruoyi-ui 是前端项目这里需要注意一下前端项目和后端项目不要放到一起因为前端项目构建的时候会生成 node_modules 文件夹这个文件夹内会有很多的文件然后后端导入就会卡死我Eclipse导入就卡死了然后改IDEA后来才想到是不是这个的原因然后将前端项目移出来就没问题了… 后端导入
首先从后端开始导入是非常简单的如果是IDEA的话那么直接打开这个项目就可以了这是一个Maven多模块项目。
如果是 Eclipse 的话使用Maven的方式导入方法如下
File - Import - Maven - Exists Maven Project然后 Root Directory 选择若依的文件夹就好了。 然后等待右下角进度条开始编译之类的完成后是这样的 我现在这个是Eclipse的Java视图在Eclipse的JavaEE视图或者使用IDEA的话那么子maven模块会在父maven模块的里面而不是展示为同级 如果你导入不能成功的话大概率是网络的问题或者Maven的问题带着报错去参考网上解决方案进行解决 前端导入
那个 ruoyi-ui 就是前端项目。
如果没有 Node 则需要先安装Node因为这个是基于 Vue 的。 在前端这个地方环境问题可搞了我好久的时间… 就照着基础的vue项目一样打开命令行进入前端项目文件夹使用 npm install 或者 cnpm install 先安装需要的依赖。 npm配置了镜像的话可以用不然就照着网上的先安装 cnpm。 网络没问题的话执行命令完成后依赖安装完后一般都会出现一大堆的Depar…什么的红色文字的错误可以先不用去管 执行 npm run dev 先看看能不能跑起来一般情况下从官网拉下里的是可以直接跑起来的。 像我这样就跑起来了会自动打开浏览器打开前端地址。
下面的 Proxy error 是我没有打开后端访问不到后端的问题。 当然我第一次其实是跑不起来的问题是没有 vue-cli-service现在能跑起来是因为全局安装了这么个东西。
如果你没有安装的话找到前端项目下的 package.json从中找到devDependencies在里面找到对应的版本 然后使用 npm install 或者 cnpm install 安装就可以了
例如
cnpm install vue/cli-service4.4.6版本号也是使用 分隔
如果缺少的是其他依赖也可以使用这样的方式解决 后端运行与配置
首先可以看一下若依官方文档给的项目结构介绍 主要模块是 admin所有的接口都在这个模块包括后面自己写的业务接口建议都放到这个模块
然后是system与common模块
system模块是将增删改查操作都放到里面了做了分层Service提供给接口的服务层)Mapper与数据库的操作带xmldomainJavaBean
common模块比较杂工具类都在这里面包括
Redis工具直接与Redis操作SpringUtils可以拿到SpringBoot的ContextgetBean之类的ServletUtils使用的ThreadLocal可以拿到当前的请求与响应接口参数之类的…
更多的需要自己发现了 framework模块是若依自己编写的包装框架这部分让我印象最深的就是权限认证了#-_-#需求是做第三方登录但要自定义登陆的话。卡了我一天多时间主要原因是因为我没接触过Spring Security关于有做第三方登陆需求的这部分我会在这篇文章中将经验写出来。 还有几个模块目前我的需求基本上不需要动这几部分ruoyi-generator 这个代码生成可以根据需要去修改配置(yml) 配置
都是SpringBoot项目首先进入 admin 模块找到 application.yml进入根据自己的需要进行修改。
比较需要注意的地方是
ruoyi:profile上传文件保存的地址spring:redis:Redis的配置
然后进入 application-druid.yml这个是数据库的配置其中要配置数据库的密码数据库名称等看注释操作就行 在父模块文件夹中RuoYi-Vue有个sql文件夹里面有两个sql需要新建一个数据库进入然后执行这两个sql
这样就可以运行后端了
运行后启动成功的话可以在控制台看到启动成功的字样的 这样将前面说前端也运行起来就可以正常使用了。 还有很多地方可以进行配置但在这里就不过多进行赘述了熟悉SpringBoot的使用基本上没有什么太大压力。 前端配置
首先可能需要配置的是后端的地址。在 vue.config.js 中 我们可能会发现前端请求的后端地址路由前缀带上了 /dev-api这是因为做了多环境使用了代理后端并不是这样的所以可以忽略这个。 其余的就是业务的编写了组件部分都在 Component 文件夹而页面布局在 layou 文件夹
我们自己写的页面在放 views 文件夹下。 使用
可以先查阅官方文档有很多使用上面都有写
代码生成
首先需要建立一个sql表表名称以在generator模块的yml配置前缀开头如果没有更改的话就是 sys_可以有多个使用逗号分隔
然后在系统工具 - 代码生成界面中导入表可视化的不多赘述了有编辑预览和生成编辑是设置生成代码的参数之类的觉得没问题就生成
生成后的压缩包里有很多文件大概好像是9个包含了一个sql菜单的增删改查sql语句以及权限等后端controller、mapper、xml…前端 api.js、view…照着目录结构放到对应目录结构就可以了。 公开接口
这部分官网文档上有不过不是很显眼我找了半天在这里记录下
直接加上 Anonymous 注解就可以了前后端分离版在类上加类内的所有接口都可访问在接口上加那就单独公开这个接口 日志
使用 Log 注解可以参考若依里的代码有很多地方都用到了
但需要注意的是必须要有登录信息也就是说公开接口不能使用这个注解。
例如
Log(title 字典数据, businessType BusinessType.EXPORT)还有一个是登录日志这个可以在登陆类中找到直接照葫芦画瓢就行了。 国际化
若依是做了国际化的但不多。用的 i18n在 admin 模块的 resources 下可以看到 i18n 文件夹有个message.properties 文件其中包含了所有的文字…
使用的话就是
MessageUtils.message(message.properties中的key)第三方登录注册
这篇文章记下这里就结尾吧因为若依是使用了很多框架的一个快速开发框架。如果熟悉若依所使用的那些框架那么上手起来没有什么太大的压力。 对于第三方登录有一个特点只会给你一个第三方的唯一用户标识而在若依中使用的Spring Security这个我没学过所以这里记录的可能并不是一个比较好的方法。
我的方法如下
第三方登录用户不存在就注册并登录存在就直接登录
注册很简单参考若依的注册接口 System 模块的 web.service.SysRegisterService但因为是第三方登录没有账号密码我的做法是生成一个随机的账号与默认密码…因为照葫芦画瓢登录是必须要账号和密码的
下面描述的是整个验证流程部分 首先查阅参考若依的登录接口在 system 模块中 web.service.SysLoginService
它登陆部分代码是这样的
/*** 登录验证* * param username 用户名* param password 密码* param code 验证码* param uuid 唯一标识* return 结果*/
public String login(String username, String password, String code, String uuid)
{// 验证码校验validateCaptcha(username, code, uuid);// 登录前置校验loginPreCheck(username, password);// 用户验证Authentication authentication null;try{UsernamePasswordAuthenticationToken authenticationToken new UsernamePasswordAuthenticationToken(username, password);AuthenticationContextHolder.setContext(authenticationToken);// 该方法会去调用UserDetailsServiceImpl.loadUserByUsernameauthentication authenticationManager.authenticate(authenticationToken);}catch (Exception e){if (e instanceof BadCredentialsException){// 记录登录日志AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message(user.password.not.match)));throw new UserPasswordNotMatchException();}else{AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));throw new ServiceException(e.getMessage());}}finally{AuthenticationContextHolder.clearContext();}AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message(user.login.success)));LoginUser loginUser (LoginUser) authentication.getPrincipal();recordLoginInfo(loginUser.getUserId());// 生成tokenreturn tokenService.createToken(loginUser);
}熟悉 Security 的看起来是非常简单的核心部分就下面这三行。
UsernamePasswordAuthenticationToken authenticationToken new UsernamePasswordAuthenticationToken(username, password);
AuthenticationContextHolder.setContext(authenticationToken);
// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
authentication authenticationManager.authenticate(authenticationToken);将账号和密码构建一个UsernamePasswordAuthenticationToken然后加入context最后去调用验证这是 Spring Security 的一个基础的做法
通过注释找到 UserDetailsServiceImpl 类就在同一个包下核心代码是这样的
这部分我已经改过了将这个类当作验证类了。
Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
{SysUser user userService.selectUserByUserName(username);if (StringUtils.isNull(user)){log.info(登录用户{} 不存在., username);throw new ServiceException(登录用户 username 不存在);}else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())){log.info(登录用户{} 已被删除., username);throw new ServiceException(对不起您的账号 username 已被删除);}else if (UserStatus.DISABLE.getCode().equals(user.getStatus())){log.info(登录用户{} 已被停用., username);throw new ServiceException(对不起您的账号 username 已停用);}Object type ServletUtils.getRequest().getAttribute(type);if (type ! null) {switch ((int) type) {case SecurityConfig.TYPE_OTHER:passwordService.validateOther(user);break;}} else {passwordService.validate(user);}return createLoginUser(user);
}看起来就好像是在这验证账号密码虽然可以这样但 Spring Security 定义的并不是这样的…
查阅 SecurityConfig 类这个是 Spring Security 的配置类在System模块的config下我们需要在意的是最后一个函数 /*** 身份认证接口*/Overrideprotected void configure(AuthenticationManagerBuilder auth) throws Exception{auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());}在这里如果没学过 Spring Security 就开始懵逼起来了所以我先去学习了Spring Security流程还是很简单反正就是在这里设置的 UserDetailsService
因为是第三方登录用户不需要输入账号密码那么我拿到的密码就是加密后的密码这样就和普通登录不一样了就会导致认证不通过
经过 debug 后发现首先走的是 AuthenticationManagerBuilder这个不用管只要知道这个里面有 ProviderManager 就行了ProviderManager 是个管理器具体的我也不记得了它里面有AuthenticationProvider列表而AuthenticationProvider这个东西就是需要解决掉的东西
private ListAuthenticationProvider providers Collections.emptyList();Spring Security 默认使用的是 DaoAuthenticationProvider跟着debug可以看到
需要注意的就两个函数一个additionalAuthenticationChecks一个retrieveUser OverrideSuppressWarnings(deprecation)protected void additionalAuthenticationChecks(UserDetails userDetails,UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {if (authentication.getCredentials() null) {this.logger.debug(Failed to authenticate since no credentials provided);throw new BadCredentialsException(this.messages.getMessage(AbstractUserDetailsAuthenticationProvider.badCredentials, Bad credentials));}String presentedPassword authentication.getCredentials().toString();if (!this.passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {this.logger.debug(Failed to authenticate since password does not match stored value);throw new BadCredentialsException(this.messages.getMessage(AbstractUserDetailsAuthenticationProvider.badCredentials, Bad credentials));}}Overrideprotected final UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication)throws AuthenticationException {prepareTimingAttackProtection();try {UserDetails loadedUser this.getUserDetailsService().loadUserByUsername(username);if (loadedUser null) {throw new InternalAuthenticationServiceException(UserDetailsService returned null, which is an interface contract violation);}return loadedUser;}catch (UsernameNotFoundException ex) {mitigateAgainstTimingAttack(authentication);throw ex;}catch (InternalAuthenticationServiceException ex) {throw ex;}catch (Exception ex) {throw new InternalAuthenticationServiceException(ex.getMessage(), ex);}}首先执行的是 retrieveUser 函数这个函数会去调用若依的 UserDetailsServiceImpl 的 loadUserByUsername 函数
执行完后会执行 additionalAuthenticationChecks 函数这个函数进行账号密码验证而若依是在 UserDetailsServiceImpl 的 loadUserByUsername 验证的验证完后还会在验证一遍因为账号密码的方式相同所以不会有什么问题但对我来说传递的密码是加密的所以不需要函数内进行加密于是就要干掉这个类在网上找了很久都找不到办法就只能自己慢慢尝试了这里直接写结果了
首先新建一个类继承 DaoAuthenticationProvider 类然后覆盖 additionalAuthenticationChecks 函数在这个函数内做自己的操作。 然后还要覆盖一个函数 supports这个是代表支持不支持当前传递的 authentication例如UsernamePasswordAuthenticationToken
我直接返回 true 就行了不支持的话那么就不会被覆盖
代码如下
/*** 自定义* 创建时间 2023年7月29日* author Shendi*/
public class MyAuthenticationProvider extends DaoAuthenticationProvider {Overrideprotected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {// 有两种类型Object type ServletUtils.getRequest().getAttribute(type);if (type ! null) {// 已经在 UserDetailsService 做了判断switch ((int) type) {case SecurityConfig.TYPE_OTHER:break;}} else {if (authentication.getCredentials() null) {this.logger.debug(Failed to authenticate since no credentials provided);throw new BadCredentialsException(this.messages.getMessage(AbstractUserDetailsAuthenticationProvider.badCredentials, Bad credentials));}String presentedPassword authentication.getCredentials().toString();if (!this.getPasswordEncoder().matches(presentedPassword, userDetails.getPassword())) {this.logger.debug(Failed to authenticate since password does not match stored value);throw new BadCredentialsException(this.messages.getMessage(AbstractUserDetailsAuthenticationProvider.badCredentials, Bad credentials));}}}/** 是否支持指定类型的 authentication */public boolean supports(Class? authentication) {
// return (MyAuthenticationProvider.class.isAssignableFrom(authentication));return true;}}然后就是在 SecurityConfig 中配置了直接上代码
Bean
public MyAuthenticationProvider myAuP() { MyAuthenticationProvider myAuP new MyAuthenticationProvider(); myAuP.setUserDetailsService(userDetailsService); myAuP.setPasswordEncoder(bCryptPasswordEncoder()); return myAuP;
}/*** 身份认证接口*/
Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{auth.authenticationProvider(myAuP());
// auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder());
}这样就ok了. END