权限控制
# 认证方式
采用 jwt token方式 进行认证,每次请求时附带 token
代表自己的身份标识,用来”认识“用户
jwt缺点是不能及时销毁,可配上 redis
进行二次验证,也方便拓展,例如:查看登录用户,踢下线用户
提示
使用 SpringScurity 工具进行验证
一开始只是自己实现登录功能,再配合 拦截器
,对所需的URL进行验证,token验证成功则通过
好处就是使用简单,但适合简单的业务,高拓展性推荐用 SpringScurity
# 权限结构
目前就三个角色:普通用户、管理员、系统管理员
使用到三个表:sys_user、role、user_role
在 sys_user
表中用字段 user_type
区分用户类型
角色固定情况下,只需一个表,用字段判断即可,后续可能加入自定义角色,所以我就用了三个表来区分权限
授权过程(大部分功能采用若依代码)
1、登录密码验证:SysUserServiceImpl.loadUserByUsername()
登录用到的userName实则是用户的账号userAccount
2、登录成功:AuthenticationFilter.successfulAuthentication()
3、创建token,把用户的登录信息存入redis,key为随机的UUID:tokenService.createToken
每次URL请求验证token正确性在:MyAuthenticationFilter
token的秘钥和有效时间可在 application.yml
修改
待完善功能
- 修改用户信息时,redis数据没同步过去
- 更改用户状态时,不会把用户踢下线
- 查看在线用户(懒得 ctrl v 若依代码)
# 拓展介绍
# JWT介绍
JWT(Json Web Token)是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准。
JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源。比如用在用户登录上
JWT最重要的作用就是对 token信息的防伪作用。
一个JWT由三个部分组成:公共部分、私有部分、签名部分。最后由这三者组合进行base64编码得到JWT。
1、 公共部分
主要是该JWT的相关配置参数,比如签名的加密算法、格式类型、过期时间等等。
2、 私有部分
用户自定义的内容,根据实际需要真正要封装的信息。
userInfo{用户的Id,用户的昵称}
3、 签名部分
SaltiP: 当前服务器的Ip地址{linux 中配置代理服务器的ip}
主要用户对JWT生成字符串的时候,进行加密{盐值}
最终组成 key+salt+userInfo = token
base64编码,并不是加密,只是把明文信息变成了不可见的字符串。其实只要用一些工具就可以把base64编码解成明文,所以不要在JWT中放入涉及私密的信息。
# 自己实现登录功能
要引入security依赖
- 注册时 BCrypt 加密密码:
public String BCrypt(String password) {
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
//加密后新密码
String newPassword = passwordEncoder.encode(password);
return newPassword;
}
2
3
4
5
6
- 密码比较,正确返回true
//密码比较
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
boolean matches = passwordEncoder.matches(password, userInfo.getPassword());
2
3
判断密码,正确生成token并返回,之后再用拦截器,对需要验证的URL判断token是否为管理员
# 拦截器使用方法
一开始采用拦截器,对相应的每次URL请求来验证 token
用法如下
- 1、添加拦截器配置
@Configuration
public class MyWebConfig implements WebMvcConfigurer {
@Bean
UserInterceptor userInterceptor() {
return new UserInterceptor();
}
@Bean
AdminInterceptor adminInterceptor() {
return new AdminInterceptor();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
// registry.addInterceptor(new AdminInterceptor()).addPathPatterns("/admin/**");
registry.addInterceptor(adminInterceptor()).addPathPatterns("/admin/**");
// 拦截器里需要使用到 @Autowired 需要Bean注入
// 没用到直接 new AdminInterceptor() 即可
WebMvcConfigurer.super.addInterceptors(registry);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- 实现拦截器方法
//具体拦截器的业务类
public class AdminInterceptor implements HandlerInterceptor {
@Autowired
private RedisTemplate<String, String> redisTemplate;
//在请求处理之前进行调用: Controller方法调用之前
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
String token = request.getHeader("X-Token");
/**
下面为判断token是否有效
有效返回:true
失效则不通过:false
**/
}
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
// System.out.println("postHandle被调用");
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
// System.out.println("afterCompletion被调用");
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 盐值加密
在用户模块,对于用户密码的保护,通常都会进行加密。我们通常对密码进行加密,然后存放在数据库中,在用户进行登录的时候,将其输入的密码进行加密然后与数据库中存放的密文进行比较,以验证用户密码是否正确。
目前,MD5和BCrypt比较流行。相对来说,BCrypt比MD5更安全,但加密更慢。
加盐
如果两个人或多个人的密码相同,加密后保存会得到相同的结果。破一个就可以破一片的密码。如果名为A的用户可以查看数据库,那么他可以观察到自己的密码和别人的密码加密后的结果都是一样,那么,别人用的和自己就是同一个密码,这样,就可以利用别人的身份登录了。
其实只要稍微混淆一下就能防范住了,这在加密术语中称为“加盐”。具体来说就是在原有材料(用户自定义密码)中加入其它成分(一般是用户自有且不变的因素),以此来增加系统复杂度。当这种盐和用户密码相结合后,再通过摘要处理,就能得到隐蔽性更强的摘要值。
# 参考资料
若依项目部分代码
SpringBoot实现Vue-admin-template登录
https://www.cnblogs.com/codeluojay/p/13123494.html
vue+springboot实现登录验证码(前后端分离)
https://blog.csdn.net/pp1981002445/article/details/110954646
基于token的springsecurity前后端分离实现
https://blog.csdn.net/weixin_42375707/article/details/113247664
springboot学习(二十二)_ 使用@Constraint注解自定义验证注解 (opens new window)
https://www.bilibili.com/video/BV1mm4y1X7Hc