0、思路
1、jwt生成的token是永久的,每次登陆的时候需要把token存数据库,所以需要拦截每次请求带过来的token是否在数据库里面。
2、验证之前需要过滤掉哪些不需要校验的url地址,避免duird和swg等这样的地址也走数据验证权限了
1、定义权限验证功能类
package com.java.core.core.security;
import java.io.IOException;
import java.util.Date;
import java.util.List;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.java.core.com.utils.http.HttpUtils;
import com.java.core.com.utils.spring.SpringUtils;
import com.java.core.com.vo.HttpResult;
import com.java.core.com.vo.HttpStatus;
import com.java.core.entity.SysUserOnline;
import com.java.core.service.system.SysUserOnlineService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.util.AntPathMatcher;
/**
* 登录认证检查过滤器
*/
public class JwtAuthenticationFilter extends BasicAuthenticationFilter {
//忽略的请求地址
private List<String> antMatchers;
//匹配字符
private boolean matchesPathPattern(String path) {
boolean result=false;
AntPathMatcher apm = new AntPathMatcher();
for (String pattern:this.antMatchers) {
result = apm.match(pattern, path);
if(result)break;
}
return result;
}
@Autowired
public JwtAuthenticationFilter(AuthenticationManager authenticationManager, List<String> antMatchers) {
super(authenticationManager);
this.antMatchers=antMatchers;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
if(!matchesPathPattern(request.getRequestURI())){
//需要校验权限的请求地址
String token = request.getHeader(JwtTokenUtils.AUTHORITIES);
SysUserOnline user = SpringUtils.getBean(SysUserOnlineService.class).selectOnlineById(token);
//为空,或者超时
if(user == null || ((new Date().getTime()-user.getStart_timestamp().getTime())/1000/60)>user.getExpire_time()){
HttpUtils.write(response, HttpResult.error(HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED,"登录凭证已经失效"));
return;
}
}
// 获取token, 并检查登录状态
SecurityUtils.checkAuthentication(request);
chain.doFilter(request, response);
}
}
2、config注册和配置
package com.java.core.web.config;
import com.java.core.core.security.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
//登录配置
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
private UserDetailsService userDetailsService;
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
// 使用自定义登录身份认证组件
auth.authenticationProvider(new JwtAuthenticationProvider(userDetailsService));
}
//配置跨域
private CorsConfigurationSource CorsConfigurationSource() {
CorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
corsConfiguration.addAllowedOrigin("*"); //同源配置,*表示任何请求都视为同源,若需指定ip和端口可以改为如“localhost:8080”,多个以“,”分隔;
corsConfiguration.addAllowedHeader("*");//header,允许哪些header,本案中使用的是token,此处可将*替换为token;
corsConfiguration.addAllowedMethod("*"); //允许的请求方法,PSOT、GET等
((UrlBasedCorsConfigurationSource) source).registerCorsConfiguration("/**", corsConfiguration); //配置允许跨域访问的url
return source;
}
//不做验证的地址
private List<String> getAntMatchers(){
List<String> list = new ArrayList<String>();
// 登录URL
list.add("/login");
//放行静态资源
list.add("/**/*.css");
list.add( "/**/*.js");
list.add("/imgs/**");
list.add("/favicon.ico");
list.add("/static/**");
//druid
list.add("/druid/**");
// Knife4j
list.add("/swagger**/**");
list.add("/webjars/**");
list.add("/v2/**");
list.add("/doc.html");
list.add("/v2/api-docs-ext/**");
return list;
}
@Override
protected void configure(HttpSecurity http) throws Exception {
// 禁用 csrf, 由于使用的是JWT,我们这里不需要csrf
http.cors().configurationSource(CorsConfigurationSource()).and().csrf().disable()
.authorizeRequests()
// 跨域预检请求
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll();
//不需要权限的页面
for (String pre: getAntMatchers()) {
http.authorizeRequests().antMatchers(pre).permitAll();
}
// 其他所有请求需要身份认证
http.authorizeRequests().anyRequest().authenticated();
// 1、登录、生成Token
http.addFilterBefore(new JwtLoginFilter(authenticationManager()), UsernamePasswordAuthenticationFilter.class);
// 2、验证Token
http.addFilterBefore(new JwtAuthenticationFilter(authenticationManager(),getAntMatchers()), UsernamePasswordAuthenticationFilter.class);
// 3、自定义登录和权限异常提示
http.exceptionHandling().accessDeniedHandler(new JwtAuthenticationAccessDenied()).authenticationEntryPoint(new JwtAuthenticationEntryPoint());
// 4、退出
http.logout().logoutSuccessHandler(new JwtLogoutSuccessHandler());
}
@Bean
@Override
public AuthenticationManager authenticationManager() throws Exception {
return super.authenticationManager();
}
}