认证成功后向请求方响应了token信息,那么请求方访问其它系统资源时,就需要带着这个token到后台,后台需要一个授权过滤器获取token信息,并解析用户权限信息,将信息封装到UsernamePasswordAuthentionToken对象存入安全上下文,方便请求时安全过滤处理
这个上下文是以线程为度,一次请求完成后 就结束
package com.itheima.security.filter;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.itheima.security.utils.JwtTokenUtil;
import io.jsonwebtoken.Claims;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
/**
* ClassName: AuthenticationFilter
* Package: com.itheima.security.config
* Description:
*
* @Author R
* @Create 2024/2/12 20:14
* @Version 1.0
*/
//每次请求 执行一次 不会重复过滤
public class AuthenticationFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
//1.获取http请求头中携带的jwt票据字符串(注意:如果用户尚未认证,则jwt票据字符串不存在)
String jwtToken = request.getHeader(JwtTokenUtil.TOKEN_HEADER);
if (StringUtils.isBlank(jwtToken)) {
//如果票据为空,可能用户准备取认证,所以不做拦截,但是此时UsernamePasswordAuthenticationToken对象未生成,那么即使放行本次请求
//后续的过滤器链中也会校验认证票据对象
filterChain.doFilter(request,response);
return;
}
//3.校验票据
Claims claims = JwtTokenUtil.checkJWT(jwtToken);
//票据失效
if (claims==null) {
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
//编码格式
response.setCharacterEncoding("UTF-8");
HashMap<String, String> info = new HashMap<>();
info.put("msg","票据失效");
info.put("data","");
info.put("code","1");
//响应
response.getWriter().write(new ObjectMapper().writeValueAsString(info));
}
//4.从合法的票据中获取用户名和权限信息
//用户名
String username = JwtTokenUtil.getUsername(jwtToken);
//权限信息 [P5, ROLE_ADMIN]
String roles = JwtTokenUtil.getUserRole(jwtToken);
//将数组格式的字符串转化成权限对象集合
String comStr = StringUtils.strip(roles, "[]");
List<GrantedAuthority> authorityList = AuthorityUtils.commaSeparatedStringToAuthorityList(comStr);
//5.组装认证成功的票据对象(认证成功时,密码位置null)
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, null, authorityList);
//6.将认证成功的票据对象保存到安全上下文中,方便后续的过滤器直接获取权限信息
//以线程为度 当前请求访问结束 那么线程回收 上下文凭证也会回收
SecurityContextHolder.getContext().setAuthentication(token);
//7.发行过滤器
filterChain.doFilter(request,response);
}
}
配置授权过滤器 过滤一切资源 因为这是资源安全的屏障 所以优先级最高
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin()//定义认证时使用form表单的方式提交数据
.and()
.logout()//登出用默认的路径登出 /logout
.permitAll()//允许所有的用户访问登录或者登出的路径,如果 .anyRequest().authenticated()注释掉,则必须添加permitAll(),否则就不能正常访问登录或者登出的路径
.and()
.csrf().disable()
.authorizeRequests();//授权方法,该方法后有若干子方法进行不同的授权规则处理
//将自定义的过滤器加入到security过滤器链,且在默认的认证过滤器之前执行
http.addFilterBefore(myAbstractAuthenticationProcessingFilter(), UsernamePasswordAuthenticationFilter.class);
//配置授权过滤器,过滤一切资源 他是资源安全的屏障 优先级最高 所以要在自定义过滤器之前
http.addFilterBefore( authenticationFilter(),MyAbstractAuthenticationProcessingFilter.class);
}