昨日回顾

Base64

1、它是干什么的?

它可以进行编码和解码

2、能解决项目中的啥问题?

往网络上传递一个二进制文件怎么办?(图片,视频)

把一个二进制文件编码一个特殊的字符串,然后进行传递。

传输二进制—》传输字符串。

服务收到特殊字符串进行解码就能得到一个二进制。

3、他不是一个加密工具,它是一个编码解密工具

加密算法

hash,MD5,Bcrpty。

自带盐值,防止暴力破解。

四个框架都干了什么事情

image-20231026091141999

ThreadLocale

1、客户端携带token发送请求

2、被拦截器拦截后开始校验token,如果token有误直接抛出异常。

3、token校验成功,从token字符串中解析出userToken对象,然后保存到THreadLocale中.

4、拦截器做放行

5、到了controller或service后,可以直接从THreadLocal中获取userToken。

6、解决安全问题还需要再controller执行之后把THreadLocal中的数据删除。

ThreadLocale是保存在当前线程里面,

一个请求发送过去,对于服务端而言就是一个线程。每个线程都有一个THreadLocal,所以他们是线程安全的。

ResponseBodyAdves

对controller返回的内容实现统一的包装(R)

RBAC权限管理系统

基于角色来控制权限的。

用户,角色,权限。

1、先找到用户的角色

2、再找到角色对应的权限,从而也就得到了用户的权限

3、权限判断的时候干的事情是

​ a、当前用户拥有哪些权限。 –》手里有几把药匙

​ b、访问这个接口需要那个权限。—》开门需要那把药匙

今日内容

画图

权限的认证

之前做的是token的认证,只要token是正确的就可以正常访问接口。

现在还要做权限,用户有了该接口的权限才能访问接口。

要做权限的认证,必须要在接口之前来完成了。在接口之前干点的事情的技术有那些?

拦截器,AOP。

使用AOP+自定义注解来实现权限的校验。

自定义注解

package com.qf.application.core.aop.annotation;

import java.lang.annotation.*;

// 这三个注解就是原注解,任何一个注解上面都会有这三个注解
@Target({ElementType.METHOD}) // 表示该注解只能加载方法上面
// *.java --->*.class --->运行
@Retention(RetentionPolicy.RUNTIME) // 该注解的作用域
@Documented // 生成JavaDoc
public @interface Permission {
}
    @GetMapping("/map")
    @Permission()
    public Map<String, Object> testMap() {
        Map<String, Object> map = new HashMap<>();
        map.put("k1", "v1");
        map.put("k2", "v2");
        return map;
    }

自定义注解后,在接口上面添加@Permission表示访问这个接口需要权限才可以。

把具体的权限(锁)写入到注解中value属性即可。

    @GetMapping("/map")
    @Permission("test:testMap") // 访问这个接口需要test:testMap权限
    public Map<String, Object> testMap() {
        Map<String, Object> map = new HashMap<>();
        map.put("k1", "v1");
        map.put("k2", "v2");
        return map;
    }

给自定义注解中添加一个方法

package com.qf.application.core.aop.annotation;

import java.lang.annotation.*;

// 这三个注解就是原注解,任何一个注解上面都会有这三个注解
@Target({ElementType.METHOD}) // 表示该注解只能加载方法上面
// *.java --->*.class --->运行
@Retention(RetentionPolicy.RUNTIME) // 该注解的作用域
@Documented // 生成JavaDoc
public @interface Permission {

    String value() default "";
    
//    String name() default "";
}

利用AOP的思想来控制权限

    @GetMapping("/map")
    @Permission("test:testMap") // 访问这个接口需要test:testMap权限
    public Map<String, Object> testMap() {
        Map<String, Object> map = new HashMap<>();
        map.put("k1", "v1");
        map.put("k2", "v2");
        return map;
    }
}

1、什么时候做权限验证?

所有的接口都需要做吗?不一定,那些需要权限验证码?

加了@Permission的接口都需要做权限验证,如果不需要就不要在接口上面加该注解。

假设总共有10个接口,3个接口加了权限验证的注解,程序该如何判断加了权限注解的接口有那些。

2、用什么方式来实现权限校验?

使用环绕通知来实现。为什么不是前置通知呢?

前置+后置=环绕的效果。

前置通知和环绕的通知区别?

它们都可以在目标方法之前干点事情?它们区别是什么?

前置通知只是在目标方法之前执行了,但是挡不住目标方法

环绕通知也可以目标方法之前执行,它可以挡住目标方法

没有权限是不能调用目标方法的,所以在这个过程中需要阻断一下。

为什么环绕可以挡住而前置挡不住?

@Before
public void before(){
    sout("在方法之前执行");
}

@Around
public void aroud(Jping p){
    
    soumt("方法之前执行");
    
    if(有权限){
         // 调用目标方法
    	p.xxxxxxx();
    }else{
	    // 在这里要提示用户没有方法这个接口的权限
    }
    soutm("目标方法之后执行");
}
package com.qf.application.core.aop.aspectj;

import com.qf.application.core.aop.annotation.Permission;
import com.qf.application.core.exception.PermissionException;
import com.qf.application.core.security.UserTokenThreadLocal;
import com.qf.application.entity.PermissionEntity;
import com.qf.application.entity.UserToken;
import com.qf.application.service.AuthoService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.Set;

/**
 * 权限控制的一个切面
 */
@Component // 把这个Bean交给IOC容器管理
@Aspect // 表示他是一个其切面
public class PermissionApsect {

    @Autowired
    private AuthoService authoService;

    // exe(public * com.qf..ss.ss(...))
    // 满足条件(加了@Permission注解的接口)进入这个方法
    @Around(value = "@annotation(permission)")// 目标方法上面加了@Permission才会进入
    public Object checkPermission(ProceedingJoinPoint proceedingJoinPoint, Permission permission) throws Throwable {

        // 1.获取当前登录用户的权限
        UserToken userToken = UserTokenThreadLocal.get();
        Integer id = userToken.getId();

        // 2.根据用户id查询他有那些权限
        Set<PermissionEntity> permissionEntitySet = authoService.getPermissionSetByUserId(id);

        // 3.获取方法当前接口需要那个权限
        String value = permission.value();

        // 4.对比
        boolean flag = false; // 默认不放行
        for (PermissionEntity permissionEntity : permissionEntitySet) {
            if (value.equals(permissionEntity.getCode())) {
                flag = true;
                break;
            }
        }

        flag = permissionEntitySet.stream().anyMatch(value::equals);

        // 5、判断是否有权限
        if (!flag) {
            throw new PermissionException(3001, "权限不足,请联系管理员");
        }

        // 6、调用目标方法
        return proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs());
    }
}

接口返回Map<String,String>报错

image-20231026142441352

image-20231026142450724

SpringMVC中默认使用Jackson实现对象的转换。

最终JSON字符串中的key/value转成的类型以COntroller上面返回的类型为主的。

如果controller上面返回的string/stirng,他最后把key和value全部的转成string类型,但是R中有一个code的值200(int),所以会抛出类型转换错误。

VUe.js环境搭建

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
		
		<!-- 引入Vue的JS文件 -->
		<script src="https://cdn.bootcss.com/vue/2.5.17-beta.0/vue.min.js"></script>
	</head>
	<body>
		
		<!-- 准备一个dev,Vue.js是单页面程序-->
		<div id="app">
			<!-- Vue熏染后的结果全部都在这个DIV里面展示-->
			{{message}}
		</div>
	</body>
	
	<script>
	
		// 初始胡Vue实例
		new Vue({
			el:"#app", // 绑定视图的展示的ID
			data:{ // 当前Vue实例中的数据
				message: 'Hello Vue xxxxxxxxx!'
			}
		});
	</script>
</html>

image-20231026160735702