认证、授权、鉴权和权限控制
0.jwt 加签->验签 加签参数中包含:头部()负载(用户名,创建时间)验签:客户端传值与服务端算一致,别人篡改什么?用户名? 验签成功标识?失败提示什么?@篡改 用户名。1.token 错误,根据token 解析荷载是会报错,其实这里应该把异常抛出,token错误!
(业务:实际报异常:暂未登录或token已经过期,因为取了用户信息,取不到报错。不取用户不会报错,即便token错误)
验签:就是根据token 反解jwt,无token或token被篡改,回报异常,说明验签失败!
/**
* 从token中获取JWT中的负载
*/
private Claims getClaimsFromToken(String token) {
Claims claims = null;
try {
claims = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
LOGGER.info("JWT格式验证失败:{}", token, e);
}
return claims;
}
1.注册后,用户信息怎么进入security的? @注册后没有存用户信息,是登录时,根据用户名查询数据库,然后存security。
2.权限怎么进去的 SecurityConfig configure 在这里从数据库查询权限,还有用户信息保存了,保存到哪里了?->AdminUserDetails 包含用户信息,权限信息,并提供对外接口
2.1 用户权限信息包含两部分 1.角色权限表 2.用户权限表,用户权限表数据怎么进去的(暂时业务没有用到)? @登录时,查数据库表,取权限赋值??不是登录时还是直接取UserDetailsService!!!。总结:登录赋值,调接口取值
3.sysUser 用户类 联系 @同一个
4.jwt 生成token原理
普通token只有过期时间与用户信息,而且会被伪造,jwt包含签名,不会被伪造。更像是一个签名
5.用户登录成功后,除了token,还有菜单权限数据,还有一个权限列表,这是个啥,权限值吗,有什么?鉴权吗? @前者只有菜单,后者包含所有,不只是权限,完整权限数据。应该不是控制权限,是控制数据是否展示。控制权限通过security注解
Map<String, Object> tokenMap = new HashMap<>();
tokenMap.put("token", token);
tokenMap.put("tokenHead", tokenHead);
tokenMap.put("authorities", SecurityUtils.getAuthentication().getAuthorities()); @包含所有权限
tokenMap.put("menuList", sysUserService.getMenuPermissionList(SecurityUtils.getUser().getId())); @只有菜单,没有按钮
tokenMap.put("uid", SecurityUtils.getUser().getId());
6.根据用户名,获取用户信息密码,权限原理,@ 是登录成功后,缓存了!!! 用户名哪里来:根据token获取,jwt中获取,因为登录生产jwt时,存入了用户名。
7.jwt 自定义鉴权过滤器 做什么 1.用户名是否正确 security中获取与token中是否一直,token中是当前登录人,为什么? @防止A登录成功,拿B的token取调接口,扩大权限,显然只判断有效期不行,2.token是否过期
8.UserDetailsService 根据用户名取用户信息,调用场景 登录时 1.登录取密码校验 2.jwt过滤器 验证用户名,token,最终从缓存中获取。但是入口在AdminUserDetails,说明在某时候数据进入了缓存 EhCache,进程内缓存,不支持分布式 @关于用户信息登录时,查库存security,其他接口直接取。
9.ums_admin_permission_relation 用户权限表中type=1含义 数据入口在->给用户分配+-权限,含义? @ 应该是增加删除权限,每次都是权限全量,与用户角色权限中权限比较,多:标记为+,少:标记为-,记的意义应该是保存记录
10.SecurityContext 取权限 赋值权限,但是权限值也是从UserDetails中获取,??此处哪里给?何时给?看上面:第8条
11.解决用户登录查库问题
首先,登录时是要查库,取用户与权限,只不过后面调接口时不必再查询数据库。
方式1.实现接口:UserDetailsService,重写方法,查询库把用户及权限信息写入,UserDetail接口。
方式2.重写配置类SecurityConfig 中 抽象类WebSecurityConfigurerAdapter userDetailsService方法
12.用户登录流程
虽然jwt配置了过滤登录请求,但是还是会走jwt过滤器(没有token 不会走jwt 认证及鉴权),然后到登录接口,根据用户名无法获得用户信息,说明用户名错误,抛出用户名密码错误。
如果密码错误,取到用户信息后,再比较密码,不正确抛出。那问题来了,为什么能获取用户信息???注册时缓存吗?@@@@查询数据库的!!每次都查询吗?应该是,之前有人说,不要每次查看?? @@@登录时,要查,调接口不再需要
13.权限来源于哪?UsernamePasswordAuthenticationToken 赋值过两次权限,都是从数据库查询用户权限,然后给到token。场景:1.登录 2.jwt过滤器(调用非登录接口时)。应该没有缓存,都是实时取表 @ 查库只有登录时,jwt过滤器也是取登录时赋值。jwt过滤器赋值权限,应该是注解鉴权时,跟这里比较。登录时,给了权限,为什么这里还要给??
if (jwtTokenUtil.validateToken(authToken, userDetails)) {
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
LOGGER.info("authenticated user:{}", username);
SecurityContextHolder.getContext().setAuthentication(authentication);
}
登录成功后,把信息赋值UserDetail接口(重写),后面调用其他接口,可以直接获取用户名。
正常的应该只需要登录时查询数据库,取出权限,后面调接口从缓存中获取,这里就涉及一个缓存刷新问题??? @@@应该是这样。
14.当未登录或者token失效,(无token或错误,无法根据token反解jwt,报异常,到下一个过滤链,进入自定义异常)访问接口时,自定义的返回结果 RestAuthenticationEntryPoint 调用接口未带token,来这里,{
"code": 401,
"data": "Full authentication is required to access this resource",
"msg": "暂未登录或token已经过期"
}
15.访问接口时,可获取当前用户所有权限,具体怎么校验有没有权限,登录后,会返回菜单和权限,这时候前端会处理,没有就看不到菜单或者按钮。但是通过接口怎么拦截?@通过security注解,@PreAuthorize 在每一个接口上加权限。
@PreAuthorize("hasAuthority('product')")
@GetMapping("myRoom")
@ApiOperation("我的直播间")
public BaseResult<ResTextLiveProgramVo> myChatRoom() {
return textLiveProgrammeService.myChatRoom();
}
16.自定义:登录失败处理器,两种方式
@Component
//public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {
public class RestAuthenticationEntryPoint implements AuthenticationFailureHandler {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
response.getWriter().println(JSON.toJSONString(BaseResult.unauthorized(authException.getMessage())));
response.getWriter().flush();
}
@Override
public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse,
AuthenticationException e) throws IOException, ServletException {
}
}
实际代码中并没有采用以上任意一种,而是在登录时,捕获了用户或密码错误异常,统一返回报错。
16.验证权限
无权限报错: 不允许访问
在接口添加权限限制 @PreAuthorize("hasAuthority('product')
问题
1.没走自定义无权限,直接报:不允许访问
2.原理是什么,异常从哪抛出的
3.不手动添加权限限制,直接调接口,怎么实现鉴权 @ 每一个接口都是手动给权限
17.登出
jwt 在有效期一直可用,无失效逻辑。
token:之前都是存在redis有失效机制。
需要做两件事
1.前端删除token
2.自定义登录处理器,并在security中配置,把请求头token设置为空。
登出过滤器
@Component
public class JwtLogoutSuccessHandler implements LogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
throws IOException, ServletException {
if (Objects.nonNull(authentication)) {
new SecurityContextLogoutHandler().logout(request, response, authentication);
}
response.setContentType("application/json;charset=UTF-8");
ServletOutputStream outputStream = response.getOutputStream();
response.setHeader("Authorization", "");
BaseResult result = BaseResult.success("");
outputStream.write(JSON.toJSONString(result).getBytes("UTF-8"));
outputStream.flush();
outputStream.close();
}
}
18 SecurityConfig jwt直接关联
在security配置类中加 入jwt过滤器
1.创建实例
@Bean
public JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter(){
return new JwtAuthenticationTokenFilter();
}
2.添加JWT filter
httpSecurity.addFilterBefore(jwtAuthenticationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
19.jwt 存在问题
https://blog.csdn.net/hanxiaotongtong/article/details/103347063
20.token刷新
1.接口请求请求头带 token, Authorization:token
2.刷新token,只是在jwt生产token中,更新了创建时间
21.jwt作用
1.认证 根据判断用户名,密码 jwt加签存了用户信息,验签验证身份
2.鉴权,权限不足 @主要由security完成,只是利用了jwt过滤器
22.spring Security 如何保存权限信息
UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails,
null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authentication);
涉及到上下文
public interface SecurityContext extends Serializable {
Authentication getAuthentication();
void setAuthentication(Authentication var1);
}
SecurityContextHolder.getContext().setAuthentication(authentication);
把权限信息给到给到上下问Context,两个地方,登录时,jwt验证后。登录后,已经把用户权限给context,为什么调接口还要赋值
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null),因为jwt判断为null才赋值,为什么为空
23.每次登录成功后,把用户及权限信息,保存到上下文,怎么实现用户隔离
参考文档:
http://www.cocoachina.com/articles/42621
https://zhengkai.blog.csdn.net/article/details/96290686
赋值权限
AdminUserDetails(自定义认证主体,包含用户信息及权限信息类。认证:及校验密码,成功后,设置创建一个实现了 Authentication接口的对象,给上下文)-> UserDetails -> UsernamePasswordAuthenticationToken ->Authentication->SecurityContext->-ThreadLocal(又保存到httpSession,取的时候从这里)-@> SecurityContextHolder
SecurityContextHolder 是如何存储 SecurityContext 的。 ThreadLocalSecurityContextHolderStrategy 怎么存储到ThreadLocal
https://www.it610.com/article/1281109153997144064.htm
https://www.bbsmax.com/A/gVdnpGr1JW/
https://blog.csdn.net/liuyanglglg/article/details/104742799
https://blog.csdn.net/weixin_34366546/article/details/87994814?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_title~default-0.no_search_link&spm=1001.2101.3001.4242
【FilterChain】【HttpSession】【SecurityContextPersistenceFilter】
SecurityContextPersistenceFilter:
该Filter的作用主要是创建一个空的SecurityContext(如果session中没有SecurityContext实例),然后持久化到session中
HttpSessionSecurityContextRepository
context 与httpsessin 绑定,为什么存在httpSession ,为什么也要保存到ThreadLocal
https://cloud.tencent.com/developer/article/1361030
https://blog.didispace.com/xjf-spring-security-4/ 比较全的源码分析
https://blog.didispace.com/xjf-spring-security-3/
SecurityContextPersistenceFilter 两个主要职责:请求来临时,创建SecurityContext安全上下文信息,请求结束时清空
请求来临时 指调用接口吗,请求结束接口执行完成吗?创建context是不是应该登录时创建。
https://www.bbsmax.com/A/gVdnpGr1JW/
https://www.it610.com/article/1281109153997144064.htm
https://zhengkai.blog.csdn.net/article/details/96290686
http://www.cocoachina.com/articles/42621
https://www.shuzhiduo.com/A/QW5YV87Gdm/
https://www.cnblogs.com/felordcn/p/12142491.html
https://blog.csdn.net/andy_zhang2007/article/details/91955225
https://my.oschina.net/u/2518341/blog/1982933
https://blog.csdn.net/qq_35067322/article/details/103209951
https://www.cnblogs.com/longfurcat/p/10293819.html
https://cloud.tencent.com/developer/article/1361030
https://blog.csdn.net/liuyanglglg/article/details/104742799
https://blog.csdn.net/weixin_34366546/article/details/87994814?utm_medium=distribute.pc_relevant.none-task-blog-2~default~baidujs_title~default-0.no_search_link&spm=1001.2101.3001.4242
https://blog.csdn.net/fengyilin_henu/article/details/84916822
https://www.cnblogs.com/longfurcat/p/10574734.html
1.使用security 依赖,配置(方法级别)
jwt:
tokenHeader: Authorization
secret: mySecret
# 60*60*24
expiration: 604800 s /60/60 168 7天有效期
tokenHead: Bearer
UserDetails:构建Authentication对象必须的信息,可以自定义,可能需要访问DB得到
SpringSecurity JWT
认证、授权、鉴权和权限控制
1.JWT作用:身份认证(token生成与校验)
生成与校验场景:生成:用户登录;校验:用户进行操作时,走JWT过滤器,从请求头中获取token,根据token反解JWT中的负载。
成功失败标志:从token中反解JWT中的负载时,如果异常,说明认证失败,无异常则认证成功
2.SpringSecurity:授权,鉴权
授权:创建用户选择权限及管理员分配权限,需要权限控制的方法上加注解:@PreAuthorize("hasAuthority('sys:role:update')")
SpringSecurity如何管理权限:接口请求走JWT过滤器,实时查数据库取用户基本信息与权限信息。
存在哪里:
怎么获取:
https://blog.csdn.net/u012702547/article/details/89629415
https://blog.csdn.net/weixin_39915815/article/details/111038973?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-6.nolandingword2&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-6.nolandingword2