目录
启动类:SpringsecurityJwtApplication 类
第一部分:编码
配置类:WebSecurityConfig类
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private JwtTokenCheckFilter jwtTokenCheckFilter ;
@Override
protected void configure(HttpSecurity http) throws Exception {
//所有请求发送给服务端都会先执行过滤器中的操作
http.addFilterBefore(jwtTokenCheckFilter, UsernamePasswordAuthenticationFilter.class) ;
//去掉防护
http.csrf().disable() ;
//表单提交
http.formLogin()
.loginProcessingUrl("/doLogin")
.successHandler(authenticationSuccessHandler()) //登录成功处理器
.failureHandler(authenticationFailureHandler())//登录失败处理器
.permitAll() ; //放行
//禁用session 因为我们使用的是前后端分离。根本不需要session,只要你具有token(jwt)就可以进行访问
http.sessionManagement().disable() ;
//放行所有的接口 因为我们使用了jwt ;所以只要请求携带了jwt 只要能被解析,那么就相当于登录成功!
http.authorizeRequests().antMatchers("/**").permitAll() ;//所以我们对所有请求进行放行 不做安全登录验证
}
//进行配置用户信息
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
//创建增加用户mxy
.withUser("mxy")
//设置密码 并且进行加密
.password(passwordEncoder().encode("123"))
//对用户进行设置访问权限
.authorities("sys:add","sys:query") ;
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder() ;
}
/**
* 登录成功时的返回值
* 1.拿到用户信息
* 2.根据用户信息生成jwt
* 3.写出去给客户端浏览器
* @return
*/
@Bean
public AuthenticationSuccessHandler authenticationSuccessHandler() {
return new AuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest request,
HttpServletResponse response,
Authentication authentication) throws IOException, ServletException {
//设置编码以及数据格式类型为json
response.setContentType("application/json;charaset=utf-8");
//获取到用户信息 进行强转
User principal = (User) authentication.getPrincipal();
//拿到我们的用户名
String username = principal.getUsername() ;
//拿到我们的权限对应的集合
Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
List<String> authStrs = new ArrayList<>(authorities.size() * 2);//使用2倍的大小 防止轻易扩容
//遍历authStrs集合 进行设置权限
authorities.forEach(new Consumer<GrantedAuthority>() {
@Override
public void accept(GrantedAuthority author) {
//该接口重写的实现方法 内置了功能
//这个方法的最终作用效果为:
// 遍历authorities集合之后 把集合中的每一个元素的权限都设置给authStrs 这个List集合中
authStrs.add(author.getAuthority());
}
});
//生成jwt jwt其实就是我们所说的token
//1.颁发token的时间
Date createTime = new Date() ;
//2.token过期的时间
Calendar now = Calendar.getInstance();
now.set(Calendar.SECOND,7200);//设置过期时间
Date expireTime = now.getTime();//获取到过期时间expireTime
//3.token中的header
HashMap<String, Object> header = new HashMap<>(4);
header.put("alg","HS256") ;
header.put("typ","JWT") ;
//4.token的载体
String jwt = com.auth0.jwt.JWT.create()
.withHeader(header)
.withIssuedAt(createTime)
.withExpiresAt (expireTime)
//自定义的进行设置一些信息到JWT中
.withClaim("username",username)//设置用户名
.withClaim("auths",authStrs) //设置用户对应的权限
//5.设置签名
.sign(Algorithm.HMAC256(JwtConstant.SIGN)) ;
//生成JWT之后,我们就要将其写出去给客户端浏览器
HashMap<String,Object> map = new HashMap<>(8) ;
//jwt其实就是我们所说的token
map.put("access_token",jwt) ;
map.put("expire_time",JwtConstant.EXPIRE_TIME) ;
map.put("type","bearer") ;//记住:token就一种类型 那就是bearer
ObjectMapper objectMapper = new ObjectMapper();
String s = objectMapper.writeValueAsString(map) ;
PrintWriter writer = response.getWriter() ;
writer.write(s) ;
writer.flush();
writer.close();
}
} ;
}
//登录失败时
@Bean
public AuthenticationFailureHandler authenticationFailureHandler() {
return new AuthenticationFailureHandler() {
@Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException e) throws IOException, ServletException {
//设置编码格式以及数据的类型为Json格式
response.setContentType("application/json;charset=utf-8");
//失败的话,那么就写出去给客户端浏览器即可
HashMap<String, Object> map = new HashMap<>(4);
map.put("code",401) ;
map.put("msg","登录失败") ;
ObjectMapper objectMapper = new ObjectMapper() ;
//把Map集合类型的数据转化为String类型的数据
String s = objectMapper.writeValueAsString(map) ;
PrintWriter writer = response.getWriter() ;
writer.write(s) ;
writer.flush();
writer.close();
}
} ;
}
}
常量类:JwtConstant 类
public interface JwtConstant {
/**
* 过期时间
*/
Integer EXPIRE_TIME = 7200 ;
/**
* 加密的字符串
*/
String SIGN = "security-jwt-sign" ;
/**
* header头里面的信息
*/
String AUTHORIZATION = "Authorization" ;
}
过滤器:JwtTokenCheckFilter类
@Configuration
public class JwtTokenCheckFilter extends OncePerRequestFilter {
/**
* 每次请求都会走这个方法
* jwt会从Headers带回来
* 解析jwt 然后把解析后的数据设置到上下文去
* 每一个请求都会进行解析,会大大的增大登录服务器的负担。所以说:jwt的性能没有session好
*/
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
//设置一个响应编码
response.setContentType("application/json;charset=utf-8");
//1.拿到url
String path = request.getRequestURI() ;
String method = request.getMethod() ;//拿到请求的类型 : Get Post Put Delete
if ("/doLogin".equals(path) && "POST".equalsIgnoreCase(method)) {
//直接进行放行,因为登录没有token
filterChain.doFilter(request,response) ;
return;
}
//验证jwt 先进行获取到 请求中携带的Headers字段中的authorization数据
//该数据的格式为:bearer+空格+jwt
String authorization = request.getHeader(JwtConstant.AUTHORIZATION) ;
//如果authorization有文本 即是有内容的意思
if (StringUtils.hasText(authorization)) {
//把authorization中的无用前缀 bearer+空格给进行删除 剩余的就是jwt的内容
String jwt = authorization.replaceAll("bearer ","") ;
//解析jwt
//第一步:先进行验证签名 验证完签名之后才可以进行解析
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(JwtConstant.SIGN)).build() ;
//jwt解析之后的类型为 DecodedJWT
DecodedJWT decodedJWT = null ;
try {
decodedJWT = jwtVerifier.verify(jwt) ;
} catch (JWTVerificationException e) {
//报错了,写一个401异常
response.getWriter().write("token验证失败");
return;
}
//拿到解析之后的jwt了 即是对应的用户信息的数据。我们给后端服务器进行设置一下这些数据
//拿到解析后数据中的用户名username的数据:
Claim usernameClaim = decodedJWT.getClaim("username") ;
String username = usernameClaim.asString(); //再开一层化为String类型
//拿到jwt中的权限数据
Claim authsClaim = decodedJWT.getClaim("auths") ;
List<String> authStrs = authsClaim.asList(String.class); //String.class 表示转化为的集合中的元素类型为String
//转变权限信息 容量设置为2倍避免轻易就扩容
ArrayList<SimpleGrantedAuthority> simpleGrantedAuthorities = new ArrayList<>(authStrs.size() * 2);
authStrs.forEach(new Consumer<String>() {
//该接口对象中重写方法的作用就是:
//把authStrs集合中的每一个String类型的元素进行遍历
// 然后转变为SimpleGrantedAuthority类型的元素设置到simpleGrantedAuthorities集合中
@Override
public void accept(String authStr) {
simpleGrantedAuthorities.add(new SimpleGrantedAuthority(authStr)) ;
}
});
//变成Security认识的对象
UsernamePasswordAuthenticationToken authenticationToken =
//该token参数为:用户名 密码和权限集合列表
new UsernamePasswordAuthenticationToken(username,null,simpleGrantedAuthorities) ;
// 怎么才能让security认识呢?
//void setAuthentication(Authentication var1);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
//代码继续往下执行 放行
filterChain.doFilter(request,response) ;
}
}
}
启动类:SpringsecurityJwtApplication 类
@SpringBootApplication
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SpringsecurityJwtApplication {
public static void main(String[] args) {
SpringApplication.run(SpringsecurityJwtApplication.class, args);
}
}
测试时的接口:TestController类
@RestController
public class TestController {
@GetMapping("test1")
@PreAuthorize("hasAuthority('sys:add')")
public String test1() {
return "add成功" ;
}
@GetMapping("test2")
@PreAuthorize("hasAuthority('sys:delete')")
public String test2() {
return "delete成功" ;
}
}