gpt4 book ai didi

java - Spring 安全 : Custom UserDetailsService not being called (using Auth0 authentication)

转载 作者:塔克拉玛干 更新时间:2023-11-03 04:21:57 27 4
gpt4 key购买 nike

我是 Spring 框架的新手,所以对于我理解中的任何漏洞,我提前表示歉意。

我正在使用 Auth0 来保护我的 API,它运行良好。我的设置和配置与 suggested setup 相同在 Auth0 文档中:

// SecurityConfig.java
@Configuration
@EnableWebSecurity(debug = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// auth0 config vars here

@Override
protected void configure(HttpSecurity http) {
JwtWebSecurityConfigurer
.forRS256(apiAudience, issuer)
.configure(http)
.authorizeRequests()
.antMatchers(HttpMethod.GET, "/api/public").permitAll()
.antMatchers(HttpMethod.GET, "/api/private").authenticated();
}
}

通过此设置,spring 安全主体被设置为来自 jwt token 的 userId (sub):auth0|5b2b...。但是,我希望将其设置为匹配的用户(来 self 的数据库),而不仅仅是 userId。我的问题是如何做到这一点。

我尝试过的

我已经尝试实现我从 this tutorial 复制的自定义数据库支持的 UserDetailsS​​ervice .但是,无论我如何尝试将它添加到我的 conf 中,它都不会被调用。我试过用几种不同的方式添加它但没有效果:

// SecurityConfig.java (changes only)

// My custom userDetailsService, overriding the loadUserByUsername
// method from Spring Framework's UserDetailsService.
@Autowired
private MyUserDetailsService userDetailsService;

protected void configure(HttpSecurity http) {
http.userDetailsService(userDetailsService); // Option 1
http.authenticationProvider(authenticationProvider()); // Option 2
JwtWebSecurityConfigurer
[...] // The rest unchanged from above
}

@Override // Option 3 & 4: Override the following method
protected void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(authenticationProvider()); // Option 3
auth.userDetailsService(userDetailsService); // Option 4
}

@Bean // Needed for Options 2 or 4
public DaoAuthenticationProvider authenticationProvider() {
DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
authProvider.setUserDetailsService(userDetailsService);
return authProvider;
}

不幸的是,由于我需要将它与 Auth0 身份验证结合起来,因此类似的“未调用 userDetails”问题都没有帮助我。

我不确定自己是否走在正确的道路上。我在 Auth0 中找不到关于这个极其常见用例的任何文档,这对我来说似乎很奇怪,所以也许我遗漏了一些明显的东西。

PS:不确定是否相关,但在初始化期间始终会记录以下内容。

Jun 27, 2018 11:25:22 AM com.test.UserRepository initDao
INFO: No authentication manager set. Reauthentication of users when changing passwords will not be performed.

编辑 1:

根据 Ashish451 的回答,我尝试复制他的 CustomUserDetailsS​​ervice,并将以下内容添加到我的 SecurityConfig 中:

@Autowired
private CustomUserDetailsService userService;

@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}

@Autowired
public void configureGlobal( AuthenticationManagerBuilder auth ) throws Exception {
auth.userDetailsService( userService );
}

不幸的是,经过这些更改后,CustomUserDetailsS​​ervice 仍未被调用。

编辑 2:

添加@Norberto Ritzmann 建议的日志记录方法时的输出:

Jul 04, 2018 3:49:22 PM com.test.repositories.UserRepositoryImpl initDao
INFO: No authentication manager set. Reauthentication of users when changing passwords will not be performed.
Jul 04, 2018 3:49:22 PM com.test.runner.JettyRunner testUserDetailsImpl
INFO: UserDetailsService implementation: com.test.services.CustomUserDetailsService

最佳答案

查看您的适配器代码,您正在配置中生成 JWT token 。我不确定 apiAudience 是什么,发行者,但我猜它生成了 JWT 的子项。您的问题是您想根据您的数据库更改 JWT sub。

我最近在 Spring Boot 应用程序中实现了 JWT 安全性。

我在从数据库中获取用户名后设置它。

为了清楚起见,我添加了带有 pkg​​ 信息的代码。

//我的适配器类。它与您的相同,只是我添加了一个过滤器。在此过滤器中,我正在验证 JWT token 。每次触发安全 Rest URL 时都会调用此过滤器。

import java.nio.charset.StandardCharsets;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.thymeleaf.spring5.SpringTemplateEngine;
import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;

import com.dev.myapp.jwt.model.CustomUserDetailsService;
import com.dev.myapp.security.RestAuthenticationEntryPoint;
import com.dev.myapp.security.TokenAuthenticationFilter;
import com.dev.myapp.security.TokenHelper;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {




@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

@Autowired
private CustomUserDetailsService jwtUserDetailsService; // Get UserDetail bu UserName

@Autowired
private RestAuthenticationEntryPoint restAuthenticationEntryPoint; // Handle any exception during Authentication

@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}

// Binds User service for User and Password Query from Database with Password Encryption
@Autowired
public void configureGlobal( AuthenticationManagerBuilder auth ) throws Exception {
auth.userDetailsService( jwtUserDetailsService )
.passwordEncoder( passwordEncoder() );
}

@Autowired
TokenHelper tokenHelper; // Contains method for JWT key Generation, Validation and many more...

@Override
protected void configure(HttpSecurity http) throws Exception {
http
.sessionManagement().sessionCreationPolicy( SessionCreationPolicy.STATELESS ).and()
.exceptionHandling().authenticationEntryPoint( restAuthenticationEntryPoint ).and()
.authorizeRequests()
.antMatchers("/auth/**").permitAll()
.anyRequest().authenticated().and()
.addFilterBefore(new TokenAuthenticationFilter(tokenHelper, jwtUserDetailsService), BasicAuthenticationFilter.class);

http.csrf().disable();
}


// Patterns to ignore from JWT security check
@Override
public void configure(WebSecurity web) throws Exception {
// TokenAuthenticationFilter will ignore below paths
web.ignoring().antMatchers(
HttpMethod.POST,
"/auth/login"
);
web.ignoring().antMatchers(
HttpMethod.GET,
"/",
"/assets/**",
"/*.html",
"/favicon.ico",
"/**/*.html",
"/**/*.css",
"/**/*.js"
);

}
}

//用户服务获取用户详情

@Transactional
@Repository
public class CustomUserDetailsService implements UserDetailsService {

protected final Log LOGGER = LogFactory.getLog(getClass());

@Autowired
private UserRepo userRepository;

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

User uu = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException(String.format("No user found with username '%s'.", username));
} else {
return user;
}
}

}

//未授权访问处理器

@Component
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint {

@Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException authException) throws IOException {
// This is invoked when user tries to access a secured REST resource without supplying any credentials
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage());
}
}

//用于验证 JWT token 的过滤器链

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.web.filter.OncePerRequestFilter;

public class TokenAuthenticationFilter extends OncePerRequestFilter {

protected final Log logger = LogFactory.getLog(getClass());

private TokenHelper tokenHelper;

private UserDetailsService userDetailsService;

public TokenAuthenticationFilter(TokenHelper tokenHelper, UserDetailsService userDetailsService) {
this.tokenHelper = tokenHelper;
this.userDetailsService = userDetailsService;
}


@Override
public void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain chain
) throws IOException, ServletException {

String username;
String authToken = tokenHelper.getToken(request);

logger.info("AuthToken: "+authToken);

if (authToken != null) {
// get username from token
username = tokenHelper.getUsernameFromToken(authToken);
logger.info("UserName: "+username);
if (username != null) {
// get user
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
if (tokenHelper.validateToken(authToken, userDetails)) {
// create authentication
TokenBasedAuthentication authentication = new TokenBasedAuthentication(userDetails);
authentication.setToken(authToken);
SecurityContextHolder.getContext().setAuthentication(authentication); // Adding Token in Security COntext
}
}else{
logger.error("Something is wrong with Token.");
}
}
chain.doFilter(request, response);
}
}

//TokenBasedAuthentication 类

import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.userdetails.UserDetails;


public class TokenBasedAuthentication extends AbstractAuthenticationToken {

private static final long serialVersionUID = -8448265604081678951L;
private String token;
private final UserDetails principle;

public TokenBasedAuthentication( UserDetails principle ) {
super( principle.getAuthorities() );
this.principle = principle;
}

public String getToken() {
return token;
}

public void setToken( String token ) {
this.token = token;
}

@Override
public boolean isAuthenticated() {
return true;
}

@Override
public Object getCredentials() {
return token;
}

@Override
public UserDetails getPrincipal() {
return principle;
}

}

//JWT 生成和验证逻辑的帮助类

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import com.dev.myapp.common.TimeProvider;
import com.dev.myapp.entity.User;


@Component
public class TokenHelper {

protected final Log LOGGER = LogFactory.getLog(getClass());

@Value("${app.name}") // reading details from property file added in Class path
private String APP_NAME;

@Value("${jwt.secret}")
public String SECRET;

@Value("${jwt.licenseSecret}")
public String LICENSE_SECRET;

@Value("${jwt.expires_in}")
private int EXPIRES_IN;

@Value("${jwt.mobile_expires_in}")
private int MOBILE_EXPIRES_IN;

@Value("${jwt.header}")
private String AUTH_HEADER;

@Autowired
TimeProvider timeProvider; // return current time. Basically Deployment time.

private SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS512;


// Generate Token based on UserName. You can Customize this
public String generateToken(String username) {
String audience = generateAudience();
return Jwts.builder()
.setIssuer( APP_NAME )
.setSubject(username)
.setAudience(audience)
.setIssuedAt(timeProvider.now())
.setExpiration(generateExpirationDate())
.signWith( SIGNATURE_ALGORITHM, SECRET )
.compact();
}


public Boolean validateToken(String token, UserDetails userDetails) {
User user = (User) userDetails;
final String username = getUsernameFromToken(token);
final Date created = getIssuedAtDateFromToken(token);
return (
username != null &&
username.equals(userDetails.getUsername())
);
}


// If Token is valid will extract all claim else throw appropriate error
private Claims getAllClaimsFromToken(String token) {
Claims claims;
try {
claims = Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token)
.getBody();
} catch (Exception e) {
LOGGER.error("Could not get all claims Token from passed token");
claims = null;
}
return claims;
}


private Date generateExpirationDate() {
long expiresIn = EXPIRES_IN;
return new Date(timeProvider.now().getTime() + expiresIn * 1000);
}

}

对于这个日志

No authentication manager set. Reauthentication of users when changing passwords 

因为您还没有实现名称为 loadUserByUsername 的方法。您将收到此日志。

编辑 1:

我正在使用过滤器链来验证 token 并在将从 token 中提取的安全上下文中添加用户....

我使用的是 JWT 而你使用的是 AuthO,只是实现方式不同。为完整的工作流程添加了完整的实现。

您专注于从 WebSecurityConfig 类实现 authenticationManagerBeanconfigureGlobal 以使用 UserService。

TokenBasedAuthentication类实现。

其他你可以跳过的东西。

关于java - Spring 安全 : Custom UserDetailsService not being called (using Auth0 authentication),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51062836/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com