第一个链接多看
https://segmentfault.com/a/1190000014479154?utm_source=tag-newest
https://www.jianshu.com/p/6abc22ec3cb8
https://blog.csdn.net/mine_song/article/details/61616259
下边是关于过滤器和自定义过滤器的:
https://bbs.csdn.net/topics/392561505
https://www.cnblogs.com/woxbwo/p/11421685.html
https://www.cnblogs.com/XueTing/p/13734792.html
shiro+jwt做登录认证和授权认证,不要和我们登录功能搞混。我们的正常登录就是通过LoginControler登录的
而 shiro的登录认证是已经登录过后,每次访问资源时我们通过判断token证明该用户携带的token是合法的,授权类似。
很重要的总结。
1. 登录:
①LoginControler,找到登录请求 @RequestMapping("/login"),前端传来 LoginDto 的参数,包含用户名、密码; 根据前端传来的用户名查找数据库对应的用户user,如果用户不存在,则抛出异常用户名不存在; 否则查看密码是否 相等,相等则表示用户名和密码正确,我们为其生成token;
②生成token:通过 JwtUtils 工具类生成 token,通过userID生成jwt;
JwtUtils 工具类主要有两个功能:
生成token generateToken(long userId) 和解析读取token getClaimByToken(String token)
③生成token后将其response.setHeader("Authorization",jwt)放入响应消息的头部,之后每次客户端头部携带token即可。
2. 那你携带token的请求后台如何认证? 就是之后每次都携带token,服务器如何识别和验证token,证明你是合法用户 每次携带token访问时,不在通过 LoginControler路径,这时候需要通过shiro+jwt做登录认证即可。
2. 登录之后,登录认证流程:
整体上流程是通过ShiroConfig包下的 顺序执行的,这也是shiroconfig配置三要素。
1、ShiroFilterFactoryBean 过滤器
2、DefaultWebSecurityManager 安全管理组
3、创建UserRealm 对象,需自定义类 Shiro与应用安全数据间(数据库)的“桥梁”或者“连接器”
(详细的是 ShiroConfig包下的ShiroFilterFactoryBean 到JwtFilter ,再到 ShiroConfig包下的DefaultWebSecurityManager,再到AccountRealm。)
第一步:先找过滤器,先把我们自定义的过滤器放到shrio中,再把我们写的shiro内置的过滤器链放到shiro中。
这是shiro内置的过滤器链写的内容:
@Bean //个人感觉:可以和过滤器合在一起写,比较直观。
public ShiroFilterChainDefinition shiroFilterChainDefinition() { //shiro过滤器链,这里把过滤器声明链单独 拉出来 写,过滤器链可以写到shiroFilterFactoryBean中,过滤链定义,通过LinkedHashMap 保证从上向下顺序执行,一般将/**放在最为下边 -->:这是一个坑呢,一不小心代码就不好使了;
DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition(); //过滤器链可以写内置的的过滤器,比如authc、perms等
Map<String, String> filterMap = new LinkedHashMap<>();//为什么使用LinkedHashMap 是HashMap的一个子类,保存了记录的插入顺序,保证了自上而下匹配,而HashMap是无序的,部分路径无法拦截,或者时有时无。
filterMap.put("/**", "jwt"); // 这里我们跳到jwt对应的 jwtFilter拦截器 进行 token 的有效性认证以及登录认证,如果登录认证返回ture,则再返回到这里
chainDefinition.addPathDefinitions(filterMap);
return chainDefinition;
第一步:JwtFilter包下的onAccessDenied方法,获得报文中的jwt,解析出jwt jwtUtils.getClaimByToken(jwt),得到如下jwt:
header={typ=JWT, alg=HS512},body={sub=7, iat=1620354034, exp=1620958834},signature=JAiLPfYv4cetlP1AX678ERwf4aPTUhEXONP9LhWTJrxjdB2PgcJvSVgIV4oXC0Cb7Ivb-_EPcet-C-NU3VJvnQ
我们都知道jwt由三部分组成:header、body(又叫载荷(Payload))、signature;
我们解析出body,初步判断jwt是否为空或者是否该jwt过期: if(claimByToken == null || jwtUtils.isTokenExpired(claimByToken.getExpiration()))
第二步: 如果jwt正常,则执行登录认证:executeLogin(servletRequest,servletResponse); 这一步包含很多步骤:
executeLogin(servletRequest,servletResponse) 细说:
executeLogin方法首先会调用creatToken方法,获得token,我们在 JwtFilter包里重写了方法,
我们将报文里的 jwt 转化成我们的JwtToken格式,其实都差不多。现在我们执行完 AuthenticationToken token = this.createToken(request, response);
如果为空,则报错,否则继续执行登录认证操作,接下来到这里:
Subject subject = this.getSubject(request, response);
subject.login(token);
执行subject.login(token),大致的说:这里之后会调用了 securityManager 的 login() 方法,然后走到 doAuthenticate() ,本质上就是调用 Realm 的方法,因此走到Realm。对应着从ShiroConfig的DefaultWebSecurityManager到AccountRealm
具体如何走的请参考:https://blog.csdn.net/saienenen/article/details/111028745
https://www.jianshu.com/p/6abc22ec3cb8
第三步:
通过简单的分析,明白了为什么 Springboot 应用使用需要 注入 DefaultWebSecurityManager,并且设置 自定义的 Realm。最终的 认证逻辑是交给 Realm 来执行的。
好了现在跳到 Realm上了:这一步才是真正执行 登录认证操作的,判断jwt的身份是否有效。首先判断用户名是否存在或者合法,再通过return new SimpleAuthenticationInfo(profile,jwtToken.getCredentials(),getName());判断密码是否正确
首先我们获得 jwt的用户名 jwtToken.getPrincipal(),这个可以看以下http://www.manongjc.com/detail/14-jmhshrgoycxclgz.html,我们在这分析下为什么 jwtToken.getPrincipal()得到的就是用户名,而getCredentials();得到的是密码
AuthenticationToken是最开始的抽象类,有getPrincipal()getCredentials()抽象方法,我们找到他的实现类UsernamePasswordToken
密码的认证通过return new SimpleAuthenticationInfo(profile,jwtToken.getCredentials(),getName());实现,具体如何实现参考:
https://blog.csdn.net/hubeilihao/article/details/106414363
https://blog.51cto.com/luchunli/1830020