平时,我们使用security框架时,都会默认使用框架的登录方式,如果想使用自定义的登录方式,就要写一个securityConfig文件,重写security里的某些方法,以达到使用自定义登录方式,SecurityConfig:
/**
* Security配置类
* 登录授权过滤器
* 重写UserDetailsService、configure的目的是为了当用户登录时是走我们自定义写的获取用户信息,以及走自定义的密码匹配规则
*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {//保留疑问,问什么要继承WebSecurityConfigurerAdapter
@Autowired
private IAdminService adminService;
@Autowired
private RestfulAccessDeniedHandler restfulAccessDeniedHandler;
@Autowired
private RestAuthorizationEntryPoint restAuthorizationEntryPoint;
/**
* 第二步,实现security完整的配置
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
//本项目使用了Jwt不需要csrf
http.csrf()
.disable()
//基于token不需要session
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/login","logout")
.permitAll()
//除上面请求,所有请求都要认证
.anyRequest()
.authenticated()
.and()
.headers()
.cacheControl();
//添加jwt登录授权拦截器
http.addFilterBefore(jwtAuthencationTokenFilter(), UsernamePasswordAuthenticationFilter.class);
//添加自定义未授权和未登录结果返回
http.exceptionHandling()
.accessDeniedHandler(restfulAccessDeniedHandler)
.authenticationEntryPoint(restAuthorizationEntryPoint);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception{
auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());
}
/**
* 重写UserDetailsService,因为其存在于WebSecurityConfigurerAdapter
*/
@Override
@Bean
public UserDetailsService userDetailsService(){
return username->{
Admin admin=adminService.getAdminByUsername(username);
if(admin!=null){
return admin;
}
return null;
};
}
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
@Bean
public JwtAuthencationTokenFilter jwtAuthencationTokenFilter(){
return new JwtAuthencationTokenFilter();
}
}
在这个文件有使用到自定义未登录时返回的状态,写在这两个包里面:
@Autowired
private RestfulAccessDeniedHandler restfulAccessDeniedHandler;
@Autowired
private RestAuthorizationEntryPoint restAuthorizationEntryPoint;
访问接口没有权限时,自定义返回结果,包RestfulAccessDeniedHandler的代码如下:
/**
* 当访问接口没有权限时,自定义返回结果
*/
@Component
public class RestfulAccessDeniedHandler implements AccessDeniedHandler {
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
response.setCharacterEncoding("UTF-8");
response.setContentType("application/json");
PrintWriter out=response.getWriter();
RespBean bean=RespBean.error("权限不足,请联系管理员!");
bean.setCode(403L);
out.write(new ObjectMapper().writeValueAsString(bean));
out.flush();
out.close();
}
}
当用户token失效时访问接口,自定义返回结果,包RestAuthorizationEntryPoint的代码如下:
/**
* 当登录或者token失效时访问接口时,自定义的返回结果
*/
@Component
public class RestAuthorizationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {
response.setCharacterEncoding("utf-8");
response.setContentType("application/json");
PrintWriter out=response.getWriter();//获取数据结果
RespBean bean=RespBean.error("尚未登录,请登录!");
bean.setCode(401L);
out.write(new ObjectMapper().writeValueAsString(bean));
out.flush();
out.close();
}
}
上面的包里面还用到了JwtAuthencationTokenFilter,这个包里只有一个方法,就是重写OncePerRequestFilter里的doFilterInternal,主要为了拦截没有token或token过期的,如果有token,重置token:
/**
* jwt登录授权过滤器
*/
public class JwtAuthencationTokenFilter extends OncePerRequestFilter {
@Value("${jwt.tokenHeader")
private String tokenHeader;
@Value("${jwt.tokenHead}")
private String tokenHead;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private UserDetailsService userDetailsService;
//这个方法主要是做前置拦截
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
//获取请求里面的tokenHeader
String authHeader=request.getHeader(tokenHeader);
//存在token
if(authHeader!=null&&authHeader.startsWith(tokenHead)){
//获取到前端传过来的token
String authToken=authHeader.substring(tokenHead.length());
String username= jwtTokenUtil.getUserNameFormToken(authToken);
//token存在用户名但未登录
if(username!=null&& SecurityContextHolder.getContext().getAuthentication()==null){
//登录
UserDetails userDetails=userDetailsService.loadUserByUsername(username);
//验证token是否有效,重新设置用户对象
if(jwtTokenUtil.validateToken(tokenHead,userDetails)){
UsernamePasswordAuthenticationToken authenticationToken=new UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities());
//重新设置到用户对象
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
}
filterChain.doFilter(request,response);
}
}
以上就可以自定义对未登录或token失效的用户做拦截了!!!