业务方法
- 参数校验
- 业务处理
- 数据封装
参数校验的方案有
1.Cookie:客户端会话技术
缺点:APP不能用、不能跨域
2.Session:服务端会话技术,底层基于Cookie
缺点:分布式环境Session不能共享、会损耗服务器的内存
3. 主流JWT令牌
-
第一部分:Header(头), 记录令牌类型、签名算法等。 例如:{"alg":"HS256","type":"JWT"}
-
第二部分:Payload(有效载荷),携带一些非敏感自定义信息、默认信息等。 例如:{"id":"1","username":"Tom"}
-
第三部分:Signature(签名),防止Token被篡改、确保安全性。将header、payload,并加入指定秘钥,通过指定签名算法计算而来。
-
- 其实在生成JWT令牌时,会对JSON格式的数据进行一次编码:进行base64编码
-
自己写的方法代码
//Map<String,Object> claims=new HashMap<>();
// claims.put("username","wusong");
//生成令牌方法 key="179d63f1-32a7-4be3-821b-766cd0e908a4" 秘钥自己设置(非常重要,不要暴露)
public String getTokenJwt(Map<String,Object> claims ,String key){
String JWT = Jwts.builder()
.addClaims(claims)
.setExpiration(new Date(System.currentTimeMillis() + 1000*60*60))
.signWith(SignatureAlgorithm.HS256, key)
.compact();
return JWT;
}
//解析令牌获取信息方法
public Map<String,Object> parserToken( String JWT,String key){
Claims claims= Jwts.parser()
.setSigningKey(key)
.parseClaimsJws(JWT).getBody();
return claims
}
当然也可以引入依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
引入工具类
public class JwtUtils {
private static String signKey = "SVRIRUlNQQ==";
private static Long expire = 24 * 60 * 60 * 1000L;
/**
* 生成JWT令牌
* @return
*/
public static String generateJwt(Map<String,Object> claims){
String jwt = Jwts.builder()
.addClaims(claims)
.signWith(SignatureAlgorithm.HS256, signKey)
.setExpiration(new Date(System.currentTimeMillis() + expire))
.compact();
return jwt;
}
/**
* 解析JWT令牌
* @param jwt JWT令牌
* @return JWT第二部分负载 payload 中存储的内容
*/
public static Claims parseJWT(String jwt){
Claims claims = Jwts.parser()
.setSigningKey(signKey)
.parseClaimsJws(jwt)
.getBody();
return claims;
}
}
Filter过滤器
-
Filter是 JavaWeb三大组件(Servlet、Filter、Listener)之一。
-
过滤器可以把对资源的请求拦截下来,从而实现一些特殊的功能
- 使用了过滤器之后,要想访问web服务器上的资源,必须先经过滤器,过滤器处理完毕之后,才可以访问对应的资源。
过滤器的基本使用操作:
- 第1步,定义过滤器 :1.定义一个类,实现 Filter 接口,并重写其所有方法。
- 第2步,配置过滤器:Filter类上加 @WebFilter 注解,配置拦截资源的路径。 引导类上加 @ServletComponentScan 开启Servlet组件支持。
- 1.init方法:过滤器的初始化方法。在web服务器启动的时候会自动的创建Filter过滤器对象,在创建过滤器对象的时候会自动调用init初始化方法,这个方法只会被调用一次。
-
- doFilter方法:这个方法是在每一次拦截到请求之后都会被调用,所以这个方法是会被调用多次的,每拦截到一次请求就会调用一次doFilter()方法。
-
- destroy方法: 是销毁的方法。当我们关闭服务器的时候,它会自动的调用销毁方法destroy,而这个销毁方法也只会被调用一次
执行流程:
过滤前的代码 ----> 放行(目标资源)---> 过滤后的代码
代码实现
@WebFilter(urlPatterns = "/*")
//配置过滤器要拦截的请求路径( /* 表示拦截浏览器的所有请求 )
//是数组可以添加多个路径
public class DemoFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("init ...");
}
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
System.out.println("拦截到了请求...");
chain.doFilter(request, response);`
}
public void destroy() {
System.out.println("destroy ... ");
}
}
@ServletComponentScan //开启对Servlet组件的支持
@SpringBootApplication
public class TliasManagementApplication {
public static void main(String[] args) {
SpringApplication.run(TliasManagementApplication.class, args);
}
}
注意事项: 在过滤器Filter中,如果不执行放行操作,将无法访问后面的资源。
放行操作:chain.doFilter(request, response);
登录例子 实现登录校验
@WebFilter(urlPatterns = "/*")是数组可以添加多个路径
public class AuthorizedFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest servletRequest1= (HttpServletRequest) servletRequest;
HttpServletResponse servletResponse1= (HttpServletResponse) servletResponse;
String requestURI = servletRequest1.getRequestURI();
//校验路径是登录则通过,进入路径内部进行密码验证
if (requestURI.contains("/login")){
filterChain.doFilter(servletRequest1,servletResponse);
return;
}
//当路径不是登录则对令牌进行验证
String token = servletRequest1.getHeader("token");
if (StrUtil.isBlank(token)){
servletResponse1.setStatus(HttpStatus.HTTP_UNAUTHORIZED);
return;
}
try {
Claims claims= (Claims) JwtUtils.parseJWT(token);
Object id=claims.getClaim("id");
System.out.println(id);
filterChain.doFilter(servletRequest1,servletResponse1);
} catch (Exception e) {
servletResponse1.setStatus(HttpStatus.HTTP_UNAUTHORIZED);
}
}
}
拦截的路径
滤器链
指的是在一个web应用程序当中,可以配置多个过滤器,多个过滤器就形成了一个过滤器链。
链上的过滤器在执行的时候会一个一个的执行,会先执行第一个Filter,放行之后再来执行第二个Filter,如果执行到了最后一个过滤器放行之后,才会访问对应的web资源。
过滤器链上过滤器的执行顺序:注解配置的Filter,优先级是按照过滤器类名(字符串)的自然排序。 比如:
- AbcFilter
- DemoFilter
这两个过滤器来说,AbcFilter 会先执行,DemoFilter会后执行。
Interceptor拦截器
是Spring的组件,要实现HandlerInterceptor接口 实现步骤:
创建一个类,实现HandlerInterceptor接口,重写抽象方法
将拦截器交给Spring管理,类上加@Component
在写一个配置类,实现 WebMvcConfigurer接口,
并且在类上加注解@Configuration //表示当前是一个spring的配置类
重写 addInterceptors方法,在内部注册拦截器
执行流程:
preHandle 方法中的 ---> 放行(目标资源)---> postHandle方法 ---> afterCompletion方法
代码实现
@Component
public class DemoInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("在handler执行之前执行");
//ture 放行 false 拦截
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("在handler只后执行,渲染视图之前执行");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("在渲染视图之后执行,作一些释放资源的动作");
}
}
@Configuration //表示当前是一个spring的配置类
public class WebConfig implements WebMvcConfigurer {
@Autowired
private DemoInterceptor demoInterceptor;
@Override
@Override public void addInterceptors(InterceptorRegistry registry) {
//注册自定义拦截器对象
registry.addInterceptor(demoInterceptor)
.addPathPatterns("/**")//设置拦截器拦截的请求路径( /** 表示拦截所有请求)
.excludePathPatterns("/login");//设置不拦截的请求路径 } }
}
}
登录例子 实现登录校验
@Component
public class AuthorizeInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//校验路径是登录则通过,进入路径内部进行密码验证,可以不写,在配置文件可以调过路径
// String requestURI = request.getRequestURI();
// if (requestURI.contains("/login")) {
// return true;
// }
//当路径不是登录则对令牌进行验证
String token = request.getHeader("token");
if (StrUtil.isBlank(token)) {
response.setStatus(HttpStatus.HTTP_UNAUTHORIZED);
return false;
}
try {
Claims claims = JwtUtils.parseJWT(token);
Object id = claims.get("id");
System.out.println(id);
return true;
} catch (Exception e) {
response.setStatus(HttpStatus.HTTP_UNAUTHORIZED);
return false;
}
}}
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private AuthorizeInterceptor authorizeInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(authorizeInterceptor)
.excludePathPatterns("/login")
.addPathPatterns("/**");
}
}
阻拦路径
目前请求的执行流程
请求 ---> Nginx ---> Tomcat ----> Filter ---> DispatcherServlet ----> Interceptor ---> Controller中的Handler
过滤器和拦截器之间的区别主要是两点:
-接口规范不同:过滤器需要实现Filter接口,而拦截器需要实现HandlerInterceptor接口。
-拦截范围不同:过滤器Filter会拦截所有的资源,而Interceptor只会拦截Spring环境中的资源.