一.整合阿里云OSS服务
1.因为启动的时候,找数据库配置信息,但是该模块不需要用到数据库,只是上传oss功能。
解决方案:
①.自己配置上数据库的信息
②.在启动类上添加属性,默认不去加载数据库配置@SpringBootApplication(exclude=DataSourceAutoConfiguration.class)
2.@Value注解能够读取配置文件中的信息,创建一个util工具类,并且在其中属性值均为private,无法使用,用spring的 InitializingBean 的 afterPropertiesSet 来初始化配置信息,这个方法将在所有的属性被初始化后调用。
3.因为文件上传难免有时会出现名字相同的文件名,为了文件不会进行覆盖加入一个随机值uuid
4.前端发送请求过来,通过BASE_API设置后端接口的地址,小数量可以设置多个BASE_API并且对其进行判断跳转不同的地址,但请求数量变多了以后,添加设置变得复杂,所以要整合Nginx对请求进行转发。
(Ps:nginx所在目录得路径中不能有中文,启动会报错)
① 整合Nginx(之前有一期博客Nginx一些知识点汇总)
② 在nginx.conf配置中进行配置接口地址信息
5.当客户端发送ajax请求发送到服务器,ajax是异步操作,当通过搜索引擎去访问一个网站,那么还没等页面加载完,搜索引擎已经将网页的关键字搜索完了,所以这种情况下不利于SEO
解决方案:使用NUXT框架搭建前台环境(服务端渲染技术),NUXT就是nodejs的框架
客户端只做一次显示,其他交由服务端进行操作,不做其他处理。(大概的意思就是JS不在前端渲染,而在服务端渲染后一次性返回)
6.前端用户页面整合NUXT
① 资源目录 assets
用于组织未编译的静态资源如 LESS、SASS 或 JavaScript。
② 组件目录 components
用于组织应用的 Vue.js 组件。Nuxt.js 不会扩展增强该目录下 Vue.js 组件,即这些组件不会像页面组件那样有 asyncData 方法的特性。
③ 布局目录 layouts
用于组织应用的布局组件。
④ 页面目录 pages
用于组织应用的路由及视图。Nuxt.js 框架读取该目录下所有的 .vue 文件并自动生成对应的路由配置。
⑤ 插件目录 plugins
用于组织那些需要在 根vue.js应用 实例化之前需要运行的 Javascript 插件。
⑥ nuxt.config.js 文件
nuxt.config.js 文件用于组织Nuxt.js 应用的个性化配置,以便覆盖默认配置。
NUXT加载过程,先加载布局页面再加载pages页面
7.未正确使用@MapperScan注解,导致Mapper映射文件没有在运行时动态代理产生相应的接口实现类。
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.********.qrcode.redis.RedisUtil' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
解决方案在相应的接口实现类上添加注解MapperScan。
① @Mapper注解:
作用:在接口类上添加了@Mapper,在编译之后会生成相应的接口实现类
添加位置:接口类上面
@Mapper
public interface UserDAO {
//代码
}
如果想要每个接口都要变成实现类,那么需要在每个接口类上加上@Mapper注解,比较麻烦,解决这个问题用@MapperScan
② @MapperScan:
作用:指定要变成实现类的接口所在的包,然后包下面的所有接口在编译之后都会生成相应的实现类
添加位置:是在Springboot启动类上面添加,
@SpringBootApplication
@EnableDiscoveryClient
@ComponentScan({"com.lys"}) //指定扫描位置
@MapperScan("com.lys.cmsservice.mapper")
public class CmsApplication {
public static void main(String[] args) {
SpringApplication.run(CmsApplication.class,args);
}
}
8.在linux上对redis的一些配置信息的更改,在启动redis服务的时候,默认是打开redis前台服务端,再打开客户端。那么在配置信息中匹配daemonize改为yes即可

9.当我的微服务配置信息中添加了redis后报如下错误,其实就是我阿里云服务器上的端口号没有开放,去打开阿里云防火墙的端口号6379即可

10.使用redis 端口6379不通,登录服务器内部执行下面的命令
1、端口监听状态 netstat -anp | grep 6379
2、服务器内部防火墙规则
iptables -nL
systemctl status firewalld
发现端口并未开放给其他ip地址只允许本机使用,输入命令行
iptables -I INPUT -p tcp --dport 6379 -j ACCEPT
开放任意ip地址即可,就可以连得上redis了。
11.单点登录SSO(SingleSignOn)
一、传统的单一服务器的用户登录服务:
① 使用session对象实现
登录成功以后,把用户数据放到session里面
判断是否登录,从session获取数据,可以获取到登录

二、集群部署(分布式)
分布式架构,各个微服务部署在不同的服务器上,不同微服务之间的登陆状态,在一个服务器上登陆过后其他服务器均存放相应的登录状态,即为单点登录

单点登录三种常用方式:
① session广播机制实现(session复制)
② cookie+redis实现
③ 使用token实现
token是什么?按照一定规则生成字符串,字符串可以包含用户信息。也可以将token设置到请求头当中放置。
12.JWT令牌
JWT自定义规则可以生成字符串,包含用户信息。
登录和登陆成功之后首页显示数据实现过程分析
1.调用接口返回token值
2.将token值放入cookie中
3.创建前端拦截器将cookie中的token值 放入http请求当中
4.当前端再次发送http请求到后端接口时已经带着token值,通过token值去获取用户信息返回到cookie中
5.从cookie中将用户信息调取出来显示
13.OAuth2
OAuth2主要解决两个问题
① 开放系统间授权,lucy存放照片到百度网盘当中,云服务打印照片需要访问百度网盘进行访问,lucy则需要授权云服务打印才可以进行打印。
类似于Token令牌的这么一个机制,产生一个字符串凭证。
② 分布式访问问题
举例:单点登录。
OAuth2解决方案:令牌机制,按照一定的规则生成字符串,字符串包含用户信息(可以使用JWT生成字符串)
OAuth2只是一种解决方案,没有定义授权处理机制,没有定义token格式,没有定义加密方法,也不是单个的协议。
14.在写课程评论的接口时对其他模块的调用
发现我在评论之后并未带出相应token中的用户信息,查看报错信息,发现在我评论的接口处是PostMapping而在调用其他模块的接口是GetMapping,大胆假设一波PostMapping中在调用其他模块时统一接口请求方式,改成PostMapping后即可实现。

@Component
@FeignClient(name="service-ucenter",fallback = UcenterClientImpl.class)
public interface UcenterClient {
//根据用户id获取用户信息
@PostMapping("/educenter/member/getInfoUc/{id}")
public UcenterMember getUcenter(@PathVariable("id") String memberId);
}
15.在整合统计模块的时候出现了如下情况,发现我的UcenterClient无法调用该模块,,但是当我刷新页面再次点击之后居然发现能成功,而且成功次数比失败次数一对一,于是当我去翻查nacos,居然发现UcenterClient微服务居然有两个实例,一个是8001,一个是8006.也就是说实例出现了两个,也就是说当Feign调用模块的时候负载均衡,并且策略是轮询。

然而果真在我的eduservice模块中发现了这个位置,我并没有调用到我的UcenterClient服务接口,而是通过mavenPom依赖导入,此前是在做课程详情整合课程评论的时候添加的Ucenter模块的接口,所以导致了出现两个实例的情况。
16.编写后端统计图表显示的接口时进行优化
首先要根据前端传过来的参数type值进行判断,因为我所查询出来的对象是根据type值而去选择相应的字段进行封装对象,也就是说该对象的属性值只有在运行的时候才会知道。那么想到反射的方法进行优化,用了一波switch case的语法糖,并且通过type先定义好方法名,然后通过反射去调用该方法即可。
public Map<String, Object> getShowData(String type, String begin, String end) {
//此时我要先去判断type的类型才可以决定daily用什么get方法添加进list集合中
String methodName = "";
switch (type){
case "login_num":
methodName = "getLoginNum";
break;
case "register_num":
methodName = "getRegisterNum";
break;
case "video_view_num":
methodName = "getVideoViewNum";
break;
case "course_num":
methodName = "getCourseNum";
break;
}
//根据条件查询对应的数据
QueryWrapper<StatisticsDaily> wrapper = new QueryWrapper<>();
wrapper.between("date_calculated",begin,end);
wrapper.select("date_calculated",type);
List<StatisticsDaily> staList = stamapper.selectList(wrapper);
//因为返回的是日期和日期对应的数量
//json两种形式,一个是对象key-value形式,一个数组形式
//前端要求数组json结构,对应后端java代码是list集合
//创建两个list集合,一个是日期list,一个是数量list
List<String> date_calculatedList = new ArrayList<>();
List<Integer> numDataList = new ArrayList<>();
//遍历查询所有数据list集合,进行封装
for (int i = 0; i < staList.size(); i++) {
StatisticsDaily daily = staList.get(i);
date_calculatedList.add(daily.getDateCalculated());
//因为该对象是运行时才知道对象当中拿到的是哪个字段值
//可以通过反射去调用方法
try {
Method method = daily.getClass().getDeclaredMethod(methodName);
Integer result = (Integer) method.invoke(daily);
numDataList.add(result);
} catch (Exception e) {
throw new GuliException(20001,"获取值失败");
}
}
Map<String,Object> map = new HashMap<>();
map.put("date_calculatedList",date_calculatedList);
map.put("numDataList",numDataList);
return map;
}
17.接口和前端的传参数问题
接口并不是要求前端返回一个封装对象,而是要求返回三个参数值,此时前端可以进行如下操作。在url接口地址中,定义一个对象并且对应属性值即可。
getDataSta(searchObj) {
return request({
url: `/staservice/sta/showData/${searchObj.type}/${searchObj.begin}/${searchObj.end}`,
method: 'get'
})
}
18.权限管理
权限管理需求:
① 菜单管理:下面有菜单列表,菜单的增删改查功能。
② 角色管理:可以对角色进行crud操作,并且为角色分配菜单
③ 用户管理:对用户的crud操作,并且为用户分配角色
数据库中有3张主表,2个关联表
① acl_role:角色表
② acl_user:用户表
③ acl_role_user:一个用户对应多个角色,一个角色对应多个用户,中间关联表
④ acl_permission:菜单表
⑤ acl_role_permission:一个用户对应多个菜单,一个菜单对应多个用户,中间关联表
多对多的关系,创建一个管理表并且在其中加入两个表的主键
acl_role -----> acl_role_user <------- acl_user
acl_role ----->acl_role_permission <-----acl_permission
16.整合SpringSecurity权限框架(filter,对请求的路径进行过滤)
主要包含两部分:用户认证和用户授权
① 用户认证:进入用户登录时候,输入用户名和密码,查询数据库,输入是否正确,如果正确就认证成功。
② 用户授权:登陆了系统,登录用户可能是不同的角色,根据不同的角色分配不同的功能。
缘由:系统的模块众多,每个模块都需要就行授权与认证,所以我们选择基于token的形式进行授权与认证:
① 用户根据用户名密码认证成功,然后获取当前用户角色的一系列权限值
② 以用户名为key,权限列表为value的形式存入redis缓存中
③ 根据用户名相关信息生成token返回,浏览器将token记录到cookie中
④ 每次调用api接口都默认将token携带到header请求头中
⑤ Spring-security解析header头获取token信息,解析token获取当前用户名,根据用户名就可以从redis中获取权限列表。
这样Spring-security就能够判断当前请求是否有权限访问