自定义secuirty拦截器
背景
很多时候security默认提供的拦截器往往不够用于我们的日常开发,所以我们经常需要自己重写某些拦截器,达到实现开发的需求
本文,以重写登录拦截器为例
相关源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
public HttpSecurity addFilterAt(Filter filter, Class<? extends Filter> atFilter) { return addFilterAtOffsetOf(filter, 0, atFilter); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
|
public class UsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/login", "POST");
private String usernameParameter = SPRING_SECURITY_FORM_USERNAME_KEY;
private String passwordParameter = SPRING_SECURITY_FORM_PASSWORD_KEY;
private boolean postOnly = true;
public UsernamePasswordAuthenticationFilter() { super(DEFAULT_ANT_PATH_REQUEST_MATCHER); }
public UsernamePasswordAuthenticationFilter(AuthenticationManager authenticationManager) { super(DEFAULT_ANT_PATH_REQUEST_MATCHER, authenticationManager); } }
|
步骤
一、创建自定义的拦截器类,继承XXX拦截器,实现重写
例如:我们重写了登录的拦截器(表单提交),按照我们需要的方式【需要json格式】,来进行修改自定义的拦截器类
需要做的事情
- 判断是否为post的请求
- 判断是否为json格式的数据
- 将json格式的数据中 获取我们需要的username , password进行认证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
|
public class LoginFilter extends UsernamePasswordAuthenticationFilter { @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
if (!request.getMethod().equals("POST")) { throw new AuthenticationServiceException("请求方法不支持" + request.getMethod()); }
if (request.getContentType().equalsIgnoreCase(MediaType.APPLICATION_JSON_VALUE)) { try {
Map<String, String> userInfo = new ObjectMapper().readValue(request.getInputStream(), Map.class); String username = userInfo.get(getUsernameParameter()); String password = userInfo.get(getPasswordParameter()); UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password); setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest); } catch (IOException e) { e.printStackTrace(); } }
return super.attemptAuthentication(request, response);
} }
|
二、在自定义的安全配置类中进行配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
|
@Bean public LoginFilter loginFilter() throws Exception { LoginFilter loFilter = new LoginFilter(); loFilter.setFilterProcessesUrl("/doLogin");
loFilter.setUsernameParameter("username"); loFilter.setPasswordParameter("password");
loFilter.setAuthenticationManager(authenticationManagerBean()); loFilter.setAuthenticationSuccessHandler((request, response, authentication) -> { Map<String, Object> result = new HashMap<>(); result.put("msg", "登录成功!"); result.put("status", "200"); result.put("用户信息", (User) authentication.getPrincipal()); response.setContentType("application/json;charset=UTF-8"); String s = new ObjectMapper().writeValueAsString(result); response.getWriter().println(s); }); loFilter.setAuthenticationFailureHandler((request, response, exception) -> { Map<String, Object> result = new HashMap<>(); result.put("msg", "登录失败!!"); result.put("status", "400"); result.put("错误信息", exception.getMessage()); response.setContentType("application/json;charset=UTF-8"); String s = new ObjectMapper().writeValueAsString(result); response.getWriter().println(s); }); return loFilter; }
|
配置自己的身份认证管理员(AuthenticationManager)
这样做的目的是为了让我们登录时进行验证的数据是从数据库或者缓存中提取的,而不是仅仅放上默认给出的数据。所以这一步是必须要进行操作的
1 2
| loFilter.setAuthenticationManager(authenticationManagerBean());
|
重写UserDetails
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @Component public class MyUserDetailsService implements UserDetailsService {
private final UserDao userDao;
@Autowired public MyUserDetailsService(UserDao userDao) { this.userDao = userDao; }
@Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userDao.loadUserByUsername(username); if (ObjectUtils.isEmpty(user)) throw new UsernameNotFoundException("用户名不正确!"); List<Role> roles = userDao.getRolesByUid(user.getId()); user.setRoles(roles); return user; } }
|
实现注入
将自己重写的DetailsService
进行依赖注入,然后交给AuthenticationManagerBuilder
1 2 3 4 5 6 7 8 9
|
private final MyUserDetailsService myUserDetailsService;
@Autowired public testConfig(MyUserDetailsService myUserDetailsService) { this.myUserDetailsService = myUserDetailsService; }
|
创建自定义的AuthenticationManager
,管理自己的DetailsService
1 2 3 4 5 6
| * 创建自己的AuthenticationManager */ @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(myUserDetailsService); }
|
将自定义的认证放到容器中,覆盖默认的
1 2 3 4 5 6 7 8
|
@Override @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); }
|
三、在安全配置类中替换默认的拦截器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() .and() .formLogin().loginPage("/loginPages") .and() .csrf().disable() ;
http.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class); }
|
将我们自定义的filter过滤器替换其中的某个过滤器(filter)
loginFilter() —change—> UsernamePasswordAuthenticationFilter