java - 使用 SpringSecurity 在 REST API 中验证 IP 地址
问题描述
我有配置了 SpringSecurity 的 Spring Boot 应用程序。它使用由 UUID.randomUUID().toString() 生成的令牌,由 AuthUser 对象中 UUIDAuthenticationService 类中的方法 login 返回。授权用户保存在 LoggedInUsers 类中。当我向 API 令牌发送请求时,通过 UUIDAuthenticationService 类中的方法 findByToken 进行验证。
最后,我为令牌验证添加了超时。现在我想添加IP地址验证。如果用户从地址 XXXX 登录(保存在 AuthUser 对象中),他应该被授权使用他的令牌仅表单地址 XXXX 怎么做?
我的 SecurityConfig.java:
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@FieldDefaults(level = PRIVATE, makeFinal = true)
class SecurityConfig extends WebSecurityConfigurerAdapter {
private static final RequestMatcher PUBLIC_URLS = new OrRequestMatcher(
new AntPathRequestMatcher("/api/login/login"),
);
private static final RequestMatcher PROTECTED_URLS = new NegatedRequestMatcher(PUBLIC_URLS);
TokenAuthenticationProvider provider;
SecurityConfig(final TokenAuthenticationProvider provider) {
super();
this.provider = requireNonNull(provider);
}
@Override
protected void configure(final AuthenticationManagerBuilder auth) {
auth.authenticationProvider(provider);
}
@Override
public void configure(final WebSecurity web) {
web.ignoring()
.requestMatchers(PUBLIC_URLS);
web.httpFirewall(defaultHttpFirewall());
}
@Override
protected void configure(final HttpSecurity http) throws Exception {
http
.sessionManagement()
.sessionCreationPolicy(STATELESS)
.and()
.exceptionHandling()
// this entry point handles when you request a protected page and you are not yet
// authenticated
.defaultAuthenticationEntryPointFor(forbiddenEntryPoint(), PROTECTED_URLS)
.and()
.authenticationProvider(provider)
.addFilterBefore(restAuthenticationFilter(), AnonymousAuthenticationFilter.class)
.authorizeRequests()
.antMatchers("/api/admin/**").hasAuthority("ROLE_ADMIN")
.antMatchers("/api/application/**").hasAnyAuthority("ROLE_ADMIN", "ROLE_EMPLOYEE", "ROLE_PORTAL")
.antMatchers("/api/rezerwacja/**").hasAnyAuthority("ROLE_ADMIN", "ROLE_EMPLOYEE")
.anyRequest()
.authenticated()
.and()
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.logout().disable();
}
@Bean
TokenAuthenticationFilter restAuthenticationFilter() throws Exception {
final TokenAuthenticationFilter filter = new TokenAuthenticationFilter(PROTECTED_URLS);
filter.setAuthenticationManager(authenticationManager());
filter.setAuthenticationSuccessHandler(successHandler());
return filter;
}
@Bean
SimpleUrlAuthenticationSuccessHandler successHandler() {
final SimpleUrlAuthenticationSuccessHandler successHandler = new SimpleUrlAuthenticationSuccessHandler();
successHandler.setRedirectStrategy(new NoRedirectStrategy());
return successHandler;
}
/**
* Disable Spring boot automatic filter registration.
*/
@Bean
FilterRegistrationBean disableAutoRegistration(final TokenAuthenticationFilter filter) {
final FilterRegistrationBean registration = new FilterRegistrationBean(filter);
registration.setEnabled(false);
return registration;
}
@Bean
AuthenticationEntryPoint forbiddenEntryPoint() {
return new HttpStatusEntryPoint(FORBIDDEN);
}
@Bean
public HttpFirewall defaultHttpFirewall() {
return new DefaultHttpFirewall();
}
}
AbstractAuthenticationProcessingFilter.java:
@FieldDefaults(level = PRIVATE, makeFinal = true)
public final class TokenAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
private static final String BEARER = "Bearer";
public TokenAuthenticationFilter(final RequestMatcher requiresAuth) {
super(requiresAuth);
}
@Override
public Authentication attemptAuthentication(
final HttpServletRequest request,
final HttpServletResponse response) {
final String param = ofNullable(request.getHeader(AUTHORIZATION))
.orElse(request.getParameter("t"));
final String token = ofNullable(param)
.map(value -> removeStart(value, BEARER))
.map(String::trim)
.orElseThrow(() -> new BadCredentialsException("Missing Authentication Token"));
final Authentication auth = new UsernamePasswordAuthenticationToken(token, token);
return getAuthenticationManager().authenticate(auth);
}
@Override
protected void successfulAuthentication(
final HttpServletRequest request,
final HttpServletResponse response,
final FilterChain chain,
final Authentication authResult) throws IOException, ServletException {
super.successfulAuthentication(request, response, chain, authResult);
chain.doFilter(request, response);
}
}
TokenAuthenticationProvider/java:
@Component
@AllArgsConstructor(access = PACKAGE)
@FieldDefaults(level = PRIVATE, makeFinal = true)
public final class TokenAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
@NonNull
UserAuthenticationService auth;
@Override
protected void additionalAuthenticationChecks(final UserDetails d, final UsernamePasswordAuthenticationToken auth) {
// Nothing to do
}
@Override
protected UserDetails retrieveUser(final String username, final UsernamePasswordAuthenticationToken authentication) {
final Object token = authentication.getCredentials();
return Optional
.ofNullable(token)
.map(String::valueOf)
.flatMap(auth::findByToken)
.orElseThrow(() -> new UsernameNotFoundException("Cannot find user with authentication token=" + token));
}
}
UUIDAuthenticationService.java:
@Service
@AllArgsConstructor(access = PACKAGE)
@FieldDefaults(level = PRIVATE, makeFinal = true)
public final class UUIDAuthenticationService implements UserAuthenticationService {
private static final Logger log = LoggerFactory.getLogger(UUIDAuthenticationService.class);
@NonNull
UserCrudService users;
@Autowired
LoginManager loginMgr;
@Override
public AuthUser login(final String username, final String password) throws Exception { //throws Exception {
AuthUser user = loginMgr.loginUser(username, password);
if (user != null) {
users.delete(user);
users.save(user);
log.info("Zalogowano użytkownika {}, przydzielono token: {}", user.getUsername(), user.getUuid());
}
return Optional
.ofNullable(user)
.orElseThrow(() -> new RuntimeException("Błędny login lub hasło"));
}
@Override
public Optional<AuthUser> findByToken(final String token) {
AuthUser user = users.find(token).orElse(null); // get();
if (user != null) {
Date now = Date.from(OffsetDateTime.now(ZoneOffset.UTC).toInstant());
int ileSekund = Math.round((now.getTime() - user.getLastAccess().getTime()) / 1000); // timeout dla tokena
if (ileSekund > finals.tokenTimeout) {
log.info("Token {} dla użytkownika {} przekroczył timeout", user.getUuid(), user.getUsername());
users.delete(user);
user = null;
}
else {
user.ping();
}
}
return Optional.ofNullable(user); //users.find(token);
}
@Override
public void logout(final AuthUser user) {
users.delete(user);
}
}
我想过在 UUIDAuthenticationService 中创建方法 findByTokenAndIp,但我不知道如何查找用户发送请求的 IP 地址以及如何在登录 UUIDAuthenticationService 中的登录方法时获取 IP 地址(我在创建 AuthUser 对象时需要它)。
解决方案
您可以在过滤器中访问,HttpServletRequest request
以便从中提取 IP。
见https://www.mkyong.com/java/how-to-get-client-ip-address-in-java/
获得 IP 后,您可以随意拒绝请求!
推荐阅读
- .net - 如何使用 AWS Lamda 和 dotnet Core 5 将 Excel 上传到 Amazon S3?
- unreal-engine4 - 生成蓝图不会生成任何东西(UE4)
- azure - 如何在 Runbook 中使用运行方式帐户发送电子邮件
- php - 在单个查询中检索多级数据 - MySQL Group Concat
- ruby - `gets':无效的参数 -
红宝石中的 (Errno::EINVAL) - highcharts - xAxis 标签在 Highcharts 中不正确可见
- python - Whatsapp 日志解析 python
- python - img =detector.findHands(img) AttributeError: 'handDetector' 对象没有属性 'findHands'
- java - HBox 和 VBox (JavaFx) 中的中心对象
- cocoa - 如何播放 macOS“垃圾箱”系统声音?