spring Security 不使用controller 實現登陸資訊檢驗返回配置 token的實現
阿新 • • 發佈:2020-08-26
結構
1WebSecurityConfigurer extends WebSecurityConfigurerAdapter 主體配置加上 2 (AuthenticationTokenFilter extends BasicAuthenticationFilter)新增 filter 過濾登陸資訊 3在登陸成功和失敗的handler寫返回值
1WebSecurityConfigurer
protected void configure(HttpSecurity http) { List<String> permitAll = authIgnoreConfig.getIgnoreUrls(); permitAll.add("/actuator/**"); permitAll.add("/error"); permitAll.add("/v2/**"); permitAll.add(Constant.TOKEN_ENTRY_POINT_URL); String[] urls = permitAll.stream().distinct().toArray(String[]::new); ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.authorizeRequests(); registry.antMatchers(urls).permitAll().anyRequest().authenticated()//url裡的連結允許通過,其他的必須有附權才能通過 .and().csrf().disable(); http // 基於token,所以不需要session .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .headers().frameOptions().disable()//防止frame巢狀攻擊 .and() .formLogin() .loginProcessingUrl(Constant.TOKEN_ENTRY_POINT_URL)//這裡寫的就是登陸的介面 重點 .successHandler(authenticationSuccessHandler()) //成功返回 重點 .failureHandler(authenticationFailureHandler())//失敗返回 重點 .and() .logout() .logoutUrl(Constant.TOKEN_LOGOUT_URL) .addLogoutHandler(logoutHandler()) .logoutSuccessUrl("/sys/logout") .permitAll() .and() .exceptionHandling() //錯誤處理 .authenticationEntryPoint(new TokenAuthenticationFailHandler()) .and() // 如果不用驗證碼,註釋這個過濾器即可 .addFilterBefore(new ValidateCodeFilter(redisTemplate,authenticationFailureHandler()),UsernamePasswordAuthenticationFilter.class) //校驗token過濾器 這裡相當於加了一個過濾器,如果不符合條件就攔截 如你的請求不帶token就攔截 重點 .addFilterBefore(new AuthenticationTokenFilter(authenticationManagerBean(),redisTemplate,customUserDetailsService), UsernamePasswordAuthenticationFilter.class); }
2AuthenticationTokenFilter 校驗所有連結是否有登陸的token 或者校驗登陸密碼資訊
import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** 校驗配置 這只是個filter 自定義了一個filter 因為沒用session 取不出來 * @Description //TODO $ * @Date 22:23 * @Author [email protected] **/ @Slf4j public class AuthenticationTokenFilter extends BasicAuthenticationFilter { private RedisTemplate redisTemplate; private CustomUserDetailsService customUserDetailsService; private ObjectMapper objectMapper = new ObjectMapper(); public AuthenticationTokenFilter(AuthenticationManager authenticationManager,RedisTemplate template,CustomUserDetailsService customUserDetailsService) { super(authenticationManager); this.redisTemplate = template; this.customUserDetailsService = customUserDetailsService; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { String token = request.getHeader(Constant.TOKEN); if(StrUtil.isBlank(token) || StrUtil.equals(token,"null")){ token = request.getParameter(Constant.TOKEN); } //如果沒有token就加token if(StrUtil.isNotBlank(token) && !StrUtil.equals(token,"null")){ Object userId = redisTemplate.opsForValue().get(token); if(ObjectUtil.isNull(userId)){ writer(response,"無效token"); return; } //從資料庫校驗 UserDetails userDetails = customUserDetailsService.loadUserByUserId((Long) userId); // UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); //放入使用者資訊 SecurityContextHolder.getContext().setAuthentication(authentication); } chain.doFilter(request, response); } @SneakyThrows public void writer(HttpServletResponse response,String msg){ response.setContentType("application/json;charset=UTF-8"); response.setStatus(HttpServletResponse.SC_OK); response.getWriter().write(objectMapper.writeValueAsString(R.error(HttpServletResponse.SC_UNAUTHORIZED,msg))); } }
3handler 設定
/** * @Description //TODO $ * @Date 21:06 * @Author [email protected] **/ //登陸成功handler 登陸成功後獲取資訊存到redis和返回資料 這裡應該是存放更新redis資料 @Slf4j @Component public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler { @Autowired private RedisTemplate redisTemplate; private ObjectMapper objectMapper = new ObjectMapper(); @SneakyThrows @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication){ String token; Long userId = 0l; if(authentication.getPrincipal() instanceof CustomUserDetailsUser){ // CustomUserDetailsUser userDetailsUser = (CustomUserDetailsUser) authentication.getPrincipal(); token = SecureUtil.md5(userDetailsUser.getUsername() + System.currentTimeMillis()); userId = userDetailsUser.getUserId(); }else { token = SecureUtil.md5(String.valueOf(System.currentTimeMillis())); } //redis放token redisTemplate.opsForValue().set(Constant.AUTHENTICATION_TOKEN + token,token,Constant.TOKEN_EXPIRE, TimeUnit.SECONDS); redisTemplate.opsForValue().set(token,userId,Constant.TOKEN_EXPIRE, TimeUnit.SECONDS); response.setCharacterEncoding(CharsetUtil.UTF_8); //網頁頭 response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE); //登陸成功了加token到瀏覽器 ? PrintWriter printWriter = response.getWriter(); printWriter.append(objectMapper.writeValueAsString(R.ok().put(Constant.TOKEN,token))); } }
失敗
/** * @Description //TODO $ * @Date 21:05 * @Author [email protected] **/ //失敗 handler @Slf4j @Component public class CustomAuthenticationFailHandler implements AuthenticationFailureHandler { private ObjectMapper objectMapper = new ObjectMapper(); @SneakyThrows @Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception){ response.setCharacterEncoding(CharsetUtil.UTF_8); response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE); PrintWriter printWriter = response.getWriter(); printWriter.append(objectMapper.writeValueAsString(R.error(exception.getMessage()))); } }