比如我登abc的用户,然后我在另一个地点也登abc的用户,那前一个登陆的在刷新或者请求接口的时候要登出,
或者是我后台改了abc的密码,那abc这个账户在刷新请求接口的时候要登出。
框架我搭好了 代码如下:
生成token工具类:
public class JwtTokenUtil {
private static InputStream inputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("jwt.jks"); // 寻找证书文件
private static PrivateKey privateKey = null;
private static PublicKey publicKey = null;
static { // 将证书文件里边的私钥公钥拿出来
try {
KeyStore keyStore = KeyStore.getInstance("JKS"); // java key store 固定常量
keyStore.load(inputStream, "123456".toCharArray());
privateKey = (PrivateKey) keyStore.getKey("jwt", "123456".toCharArray()); // jwt 为 命令生成整数文件时的别名
publicKey = keyStore.getCertificate("jwt").getPublicKey();
} catch (Exception e) {
e.printStackTrace();
}
}
public static String generateToken(String subject, int expirationSeconds, String salt) {
return Jwts.builder()
.setClaims(null)
.setSubject(subject)
.setExpiration(new Date(System.currentTimeMillis() + expirationSeconds * 1000))
// .signWith(SignatureAlgorithm.HS512, salt) // 不使用公钥私钥
.signWith(SignatureAlgorithm.RS256, privateKey)
.compact();
}
public static String parseToken(String token, String salt) {
String subject = null;
try {
Claims claims = Jwts.parser()
// .setSigningKey(salt) // 不使用公钥私钥
.setSigningKey(publicKey)
.parseClaimsJws(token).getBody();
subject = claims.getSubject();
} catch (Exception e) {
}
return subject;
}
}
登陆成功后生成JTWtoken :
@Component
public class AjaxAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
ResponseBase responseBase = new ResponseBase();
responseBase.setCode("200");
responseBase.setMassage("Login Success!");
String jwtToken = JwtTokenUtil.generateToken(String.valueOf(authentication.getPrincipal()), 300, "_secret");
responseBase.setJwtToken(jwtToken);
httpServletResponse.getWriter().write(JSON.toJSONString(responseBase));
}
}
监听器:
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
@Autowired
AdminSysUserSecurityService adminSysUserSecurityService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
String authHeader = request.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Bearer ")) {
final String authToken = authHeader.substring("Bearer ".length());
String username = JwtTokenUtil.parseToken(authToken, "_secret");
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = adminSysUserSecurityService.loadUserByUsername(username);
if (userDetails != null) {
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
}
}
chain.doFilter(request, response);
}
}
@Component("rbacauthorityservice")
public class RbacAuthorityService {
public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
Object userInfo = authentication.getPrincipal();
boolean hasPermission = false;
if (userInfo instanceof UserDetails) {
String username = ((UserDetails) userInfo).getUsername();
//获取资源
Set<String> urls = new HashSet();
urls.add("/**/**"); // 这些 url 都是要登录后才能访问,且其他的 url 都不能访问!
Set set2 = new HashSet();
Set set3 = new HashSet();
AntPathMatcher antPathMatcher = new AntPathMatcher();
for (String url : urls) {
if (antPathMatcher.match(url, request.getRequestURI())) {
hasPermission = true;
break;
}
}
return hasPermission;
} else {
return false;
}
}
}
配置:
@Configuration
public class AdminWebSecurityConfig extends WebSecurityConfigurerAdapter {
@Value("${pay.strength}")
private int strength;
@Autowired
private AdminSysUserSecurityService adminSysUserSecurityService;
@Bean
public PasswordEncoder passwordEncoder(){
// return NoOpPasswordEncoder.getInstance();//不对密码加密
return new BCryptPasswordEncoder(strength);//秘钥迭代次数为 2^strength,strength取值在 4-31 之间,默认10
}
@Autowired
AjaxAuthenticationEntryPoint authenticationEntryPoint; // 未登陆时返回 JSON 格式的数据给前端(否则为 html)
@Autowired
AjaxAuthenticationSuccessHandler authenticationSuccessHandler; // 登录成功返回的 JSON 格式数据给前端(否则为 html)
@Autowired
AjaxAuthenticationFailureHandler authenticationFailureHandler; // 登录失败返回的 JSON 格式数据给前端(否则为 html)
@Autowired
AjaxLogoutSuccessHandler logoutSuccessHandler; // 注销成功返回的 JSON 格式数据给前端(否则为 登录时的 html)
@Autowired
AjaxAccessDeniedHandler accessDeniedHandler; // 无权访问返回的 JSON 格式数据给前端(否则为 403 html 页面)
@Autowired
JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter; // JWT 拦截器
@Autowired
SelfAuthenticationProvider provider; // 自定义安全认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 加入自定义的安全认证
auth.authenticationProvider(provider);
}
@Override
public void configure(HttpSecurity http)throws Exception{
//开启HttpSecurity配置
http.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 使用 JWT,关闭token
.and()
.httpBasic().authenticationEntryPoint(authenticationEntryPoint)
.and()
.authorizeRequests()
// .antMatchers("/db/**").hasRole("DB")
// .antMatchers("/user/**").hasRole("USER")
// .antMatchers("/admin/**").hasRole("ADMIN")
// .antMatchers("/user/**").access("hasAnyRole('ADMIN','ROOT')")
// .antMatchers("/db/**").access("hasAnyRole('ADMIN') and hasRole('DBA')")
//除了前面路径,用户访问其他URL都必须认证后访问
.anyRequest()
.access("@rbacauthorityservice.hasPermission(request,authentication)") // RBAC 动态 url 认证
.and()
//开启表单登录,同事配置了登入接口为/login
.formLogin()
/* //该接口主要方便ajax或者移动端登入
.loginProcessingUrl("/login")
//设置登录成功跳转页面,error=true控制页面错误信息的展示
.successForwardUrl("/index").failureUrl("/login?error=true")
*/ //登入接口都不需要认证
.successHandler(authenticationSuccessHandler) // 登录成功
.failureHandler(authenticationFailureHandler) // 登录失败
.permitAll()
.and()
//开启注销登入配置
.logout()
.logoutSuccessHandler(logoutSuccessHandler)
.permitAll();
http.rememberMe().rememberMeParameter("remember-me")
.userDetailsService(adminSysUserSecurityService).tokenValiditySeconds(300);
http.exceptionHandling().accessDeniedHandler(accessDeniedHandler); // 无权访问 JSON 格式的数据
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); // JWT Filter
}
但是我重新调用新的登陆接口,相同用户名获取新的token后,原来的token依旧可以请求。
所以要在哪边配置 用户其它地方登陆,或者用户密码变更,jtwtoken失效