前言:
而今你们对“过滤器验证用户登录怎么弄”可能比较重视,你们都想要学习一些“过滤器验证用户登录怎么弄”的相关知识。那么小编在网络上搜集了一些关于“过滤器验证用户登录怎么弄””的相关知识,希望各位老铁们能喜欢,我们一起来学习一下吧!本文开始讲springsecurity框架登录认证授权的一些知识点。为什么没有说shiro这个框架,主要是现在大部分的主流项目中,特别是前后端分离的项目中权限框架一般都用的是springsecurity,比较适合,然后还有一点,就是一些开源框架(比如最新版本的工作流引擎activiti7)跟springsecurity的整合,促使我对这个springsecurity进一步加深了解。还有一个前提,就是本文是完全基于前后端分离的基础上写作,未分离项目可以做借鉴。
按照正常的思维,一个权限框架要解决的问题是:登录以及还有登录之后的访问。这个需要怎么实现,其实就是一串过滤器跟拦截器。用户没有登录,进行拦截;用户登录之后,带着证书登陆,拦截器先判断是否有证书,然后再判断证书是否合法,有一个不满足,都进行拦截。springsecurity这个框架其实本身封装的就是一连串的过滤器跟拦截器。这里借鉴一下网上的一张原理图片:
首先,我们先说登录。官方术语叫认证Authentication。主要是通过AuthenticationManager接口进行认证。(本文主要将通过用户名密码进行认证,其他认证方式后续文章会有说明。)AuthenticationManager的默认实现是ProviderManager,它又委托AuthenticationProvider实例来实现认证,我们通常用到的认证方式就是通过DaoAuthenticationProvider来认证的。(上边这几句话有点难以理解,实在不理解的话直接跳过,总之就是通过下边这个接口进行认证的,然后登录接口调用这个接口进行认证。)
public interface AuthenticationManager { Authentication authenticate(Authentication authentication)throws AuthenticationException;}
// 用户登录认证Authentication authentication = null;try { // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername(这个实现类后边会有说明,这里主要讲登陆逻辑) authentication = authenticationManager .authenticate(new UsernamePasswordAuthenticationToken(username, password));}catch (Exception e){ if (e instanceof BadCredentialsException) { //抛出登录异常 throw new UserPasswordNotMatchException(); }else{ //抛出自定义异常 throw new CustomException(e.getMessage()); }}
接下来,我们要把这套登录整合到我们的系统,需要用到我们自己的用户角色权限表。springsecurity中默认使用UserDetailsService来获取用户权限信息,我们需要自己实现这个接口,然后注入到认证接口中。
public class UserDetailsServiceImpl implements UserDetailsService{ private static final Logger log = LoggerFactory.getLogger(UserDetailsServiceImpl.class); @Autowired private ISysUserService userService; @Autowired private SysPermissionService permissionService; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { SysUser user = userService.selectUserByUserName(username);//查询用户信息 if (StringUtils.isNull(user)){ throw new UsernameNotFoundException("登录用户:" + username + " 不存在"); }else if (UserStatus.DELETED.getCode().equals(user.getDelFlag())) { throw new BaseException("对不起,您的账号:" + username + " 已被删除"); }else if (UserStatus.DISABLE.getCode().equals(user.getStatus())){ throw new BaseException("对不起,您的账号:" + username + " 已停用"); } //讲用户信息跟权限信息统一封装到UserDetails new UserDetails(user, permissionService.getMenuPermission(user)); }}
public class SecurityConfig extends WebSecurityConfigurerAdapter{ /** * 身份认证接口 */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception{ //注入身份认证接口,通过bCryptPasswordEncoder密码加密认证 auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder()); } /** * 强散列哈希加密实现 */ @Bean public BCryptPasswordEncoder bCryptPasswordEncoder() { return new BCryptPasswordEncoder(); }}
其次,登录认证都说完了,我们开始说过滤拦截,通过继承自WebSecurityConfigurerAdapter,对Spring Security自定义配置添加过滤器,下边我们直接在代码里做注释说明:
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)public class SecurityConfig extends WebSecurityConfigurerAdapter{ @Autowired private UserDetailsService userDetailsService;//自定义用户认证逻辑 @Autowired private AuthenticationEntryPointImpl unauthorizedHandler;//认证失败处理类 @Autowired private LogoutSuccessHandlerImpl logoutSuccessHandler;//退出处理类 @Autowired private JwtAuthenticationTokenFilter authenticationTokenFilter;//token认证过滤器 @Autowired private CorsFilter corsFilter;//跨域过滤器 @Override protected void configure(HttpSecurity httpSecurity) throws Exception{ httpSecurity // CSRF禁用,因为不使用session .csrf().disable() // 认证失败处理类 .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and() // 基于token,所以不需要session .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() // 过滤请求 .authorizeRequests() // 对于登录login 验证码captchaImage 允许匿名访问 .antMatchers("/login", "/captchaImage").anonymous() .antMatchers(HttpMethod.GET,"/*.html","/**/*.html", "/**/*.css", "/**/*.js").permitAll() .antMatchers("/processDefinition/**").permitAll() .antMatchers("/activitiHistory/**").permitAll() .antMatchers("/profile/**").anonymous() .antMatchers("/common/download**").anonymous() .antMatchers("/common/download/resource**").anonymous() .antMatchers("/swagger-ui.html").anonymous() .antMatchers("/swagger-resources/**").anonymous() .antMatchers("/webjars/**").anonymous() .antMatchers("/*/api-docs").anonymous() .antMatchers("/druid/**").anonymous() // 除上面外的所有请求全部需要鉴权认证 .anyRequest().authenticated() .and().headers().frameOptions().disable(); httpSecurity.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler); // 添加JWT filter httpSecurity.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); // 添加CORS filter httpSecurity.addFilterBefore(corsFilter, JwtAuthenticationTokenFilter.class); httpSecurity.addFilterBefore(corsFilter, LogoutFilter.class); }}
最后,我们对JwtAuthenticationTokenFilter过滤器做主要说明,因为这是用户登录之后,每次访问接口的时候,都需要通过这个接口进行token验证,并把用户信息放入到SecurityContextHolder上下文中,然后后台服务就可以直接在上下文中获取用户信息。
@Componentpublic class JwtAuthenticationTokenFilter extends OncePerRequestFilter{ @Autowired private TokenService tokenService; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { //从请求头中获取token,并从缓存中查询用户信息(缓存中用户信息是在用户登录后放入缓存,可以加快查询效率) LoginUser loginUser = tokenService.getLoginUser(request); if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())){ tokenService.verifyToken(loginUser);//验证token,同时自动刷新token使用时间 //以下逻辑就是把通过token验证的用户信息放入到上下文中,后台服务可以直接通过上下文获取当前用户 UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authenticationToken); } chain.doFilter(request, response); }}
//后台服务获取当前用户代码Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();if (principal instanceof UserDetails) { String username = ((UserDetails)principal).getUsername();} else { String username = principal.toString();}
以上,就是我对springsecurity安全框架做出的总结。另外,文章里的部分代码,是借鉴若依大佬的RuoyiVue这套前后端分离框架的,完全开源的,有不对的地方,请大家指正。后边文章,我会写前端如何跟后端进行token接口交互的文章,整合前端。
标签: #过滤器验证用户登录怎么弄