JWT
全称是JSON Web Token
,如果从字面上理解感觉是基于JSON
格式用于网络传输的令牌。实际上,JWT
是一种紧凑的Claims
声明格式,旨在用于空间受限的环境进行传输,常见的场景如HTTP
授权请求头参数和URI
查询参数。JWT
会把Claims
转换成JSON
格式,而这个JSON
内容将会应用为JWS
结构的有效载荷或者应用为JWE
结构的(加密处理后的)原始字符串,通过消息认证码(Message Authentication Code
或者简称MAC
)和/或者加密操作对Claims
进行数字签名或者完整性保护。
cn.hutool
hutool-all
5.8.16
这里就不过多写了,依旧是跟上一边的文章一样
package com.aaa.filter;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
import com.aaa.until.ResponseMsg;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
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.io.PrintWriter;
import java.util.List;
import java.util.stream.Collectors;
@Component
public class JWTFilter extends OncePerRequestFilter {
/**
* 重写的doFilterInternal方法
* 解析token并验证用户信息
* 如果验证成功,则保存用户信息并放行
* 如果验证失败,则返回错误信息
* 如果token为空且请求路径不在白名单中,则返回错误信息
*/
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
// 白名单路径
String[] whitename = {"/login"};
// 获取请求头中的token
String token = request.getHeader("token");
// 如果token不为空
if (StringUtils.isNotBlank(token)) {
// 验证token是否有效
boolean verify = JWTUtil.verify(token, "user".getBytes());
// 如果验证通过
if (verify) {
// 解析token,获取用户名和资源信息
JWT jwt = JWTUtil.parseToken(token);
String username = (String) jwt.getPayload("username");
List resources = (List) jwt.getPayload("resources");
// 将资源信息转换为SimpleGrantedAuthority列表
List resourceList = resources.stream().map(res -> new SimpleGrantedAuthority(res)).collect(Collectors.toList());
// 保存用户信息
UsernamePasswordAuthenticationToken userPwdToken = new UsernamePasswordAuthenticationToken(username, null, resourceList);
SecurityContextHolder.getContext().setAuthentication(userPwdToken);
// 放行
filterChain.doFilter(request, response);
} else {
// 验证失败,返回错误信息
ResponseMsg responseMsg = new ResponseMsg(401, "没有登陆", null);
printJsonData(response, responseMsg);
}
} else {
// 如果token为空,检查请求路径是否在白名单中
String requestURI = request.getRequestURI();
// 如果在白名单中,放行
if (ArrayUtils.contains(whitename, requestURI)) {
filterChain.doFilter(request, response);
} else {
// 如果不在白名单中,返回错误信息
ResponseMsg responseMsg = new ResponseMsg(401, "没有登陆", null);
printJsonData(response, responseMsg);
}
}
}
public void printJsonData(HttpServletResponse response, ResponseMsg responseMsg) {
try {
response.setContentType("application/json;charset=utf8"); // 设置响应内容类型为JSON,并指定编码为UTF-8
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(responseMsg); // 使用ObjectMapper将ResponseMsg对象转化为JSON字符串
PrintWriter writer = response.getWriter();
writer.print(json); // 将JSON字符串写入响应输出流
writer.flush();//刷新
writer.close();//关闭
} catch (Exception e) {
e.printStackTrace(); // 打印异常堆栈信息
}
}
}
package com.aaa.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
@Configuration
public class CrossConfig {
@Bean
public CorsFilter corsFilter() {
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
final CorsConfiguration corsConfiguration = new CorsConfiguration();
//corsConfiguration.setAllowCredentials(true);允许携带cookie
corsConfiguration.addAllowedHeader("*"); // 允许所有的头
corsConfiguration.addAllowedOrigin("*"); // 允许所有的请求源
corsConfiguration.addAllowedMethod("*"); // 所欲的方法 get post delete put
source.registerCorsConfiguration("/**", corsConfiguration); // 所有的路径都允许跨域
return new CorsFilter(source);
}
}
package com.aaa.config;
import cn.hutool.jwt.JWTUtil;
import com.aaa.filter.JWTFilter;
import com.aaa.until.ResponseMsg;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
protected UserDetailsService userDetailsService;
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Resource
public JWTFilter jwtFilter;
@Override
protected void configure(HttpSecurity http) throws Exception {
//.usernameParameter("username")//登陆账号
//.passwordParameter("userpassword");//登陆密码
//.defaultSuccessUrl("/test");//登陆成功的跳转路径
// 配置登录页面和登录处理路径
http.formLogin().loginPage("/login.html")//路径前面必须加 /
.loginProcessingUrl("/login")//跟提交的路径一样
// 登录成功的处理逻辑
.successHandler((request, response, authentication) ->{
Collection extends GrantedAuthority> authorities = authentication.getAuthorities();//获取资源信息
List resources = authorities.stream().map(auth -> auth.getAuthority()).collect(Collectors.toList());//转为stream流变成list
Map map = new HashMap<>();
map.put("username",authentication.getName());
map.put("resources",resources);
//生成token
String token = JWTUtil.createToken(map, "user".getBytes());
ResponseMsg responseMsg = new ResponseMsg(200,"登陆成功",token);
//响应返回信息
printJsonData(response,responseMsg);
})
// 登录失败的处理逻辑
.failureHandler((request, response, exception)->{
ResponseMsg responseMsg = new ResponseMsg(400,"验证失败");
printJsonData(response,responseMsg);
});
// 配置权限不允许的处理逻辑
http.exceptionHandling().accessDeniedHandler((request, response, accessDeniedException) -> {
ResponseMsg responseMsg = new ResponseMsg(403,"权限不允许",null);
printJsonData(response,responseMsg);
});
// 配置不需要验证的路径
http.authorizeRequests().antMatchers("/login.html", "login").permitAll();//不用验证
//http.authorizeRequests().antMatchers("/test").hasRole("test");//必须有哪个权限才能访问
//http.authorizeRequests().antMatchers("/test").hasAnyAuthority("resource");//必须有哪个资源才能访问
// 配置其他路径需要验证
http.authorizeRequests().anyRequest().authenticated();
// 添加JWT过滤器
http.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class);
//关闭csrf保护
http.csrf().disable();
// 配置跨域
http.cors();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
public void printJsonData(HttpServletResponse response, ResponseMsg responseMsg) {
try {
response.setContentType("application/json;charset=utf8"); // json格式 编码是中文
ObjectMapper objectMapper = new ObjectMapper();
String s = objectMapper.writeValueAsString(responseMsg);// 使用ObjectMapper将result转化json为字符串
PrintWriter writer = response.getWriter();
writer.print(s);
writer.flush();
writer.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
也是跟上一篇的笔记一样
不要忘了在路由下添加login页面
提交
重置
验证token是否存在,如果有的话就在请求头里面添加token
// 请求拦截器
instance.interceptors.request.use(config => {
if (sessionStorage.getItem("token")){
let token = sessionStorage.getItem("token");
config.headers['token'] = token;
}
return config;
}, error => {
return Promise.reject(error);
});
从前端页面输入账号向后端发送跨域请求,后端接受参数验证是否在数据库中存在,如果存在就去把用户在数据库中的角色权限和资源权限查出来,保存在Security中.登陆成功后就把用户的信息用JWTUtil.createToken()方法生成token,返回给前端,前端拿到token,前端再次向后端发起请求时携带token,后端识别,如果正确的话就放行,不正确就访问失败