Java Spring安全性:在UserDetailsService方法中获取密码
为了获得我的帐户,我需要登录一个外部spring应用程序。为什么需要它并不重要,但为了在API上执行/login调用,我需要在UserDetailsService方法中获取密码。这是我的安全设置:Java Spring安全性:在UserDetailsService方法中获取密码,java,spring,spring-security,Java,Spring,Spring Security,为了获得我的帐户,我需要登录一个外部spring应用程序。为什么需要它并不重要,但为了在API上执行/login调用,我需要在UserDetailsService方法中获取密码。这是我的安全设置: //https://auth0.com/blog/implementing-jwt-authentication-on-spring-boot/ @EnableWebSecurity public class WebSecurity extends WebSecurityConfigurerAdapt
//https://auth0.com/blog/implementing-jwt-authentication-on-spring-boot/
@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
private UserDetailsService userDetailsService;
private BCryptPasswordEncoder bCryptPasswordEncoder;
//Constructor gets authLogic for external authentication
@Autowired
public WebSecurity(@Qualifier("authLogic") UserDetailsService userDetailsService){
this.userDetailsService = userDetailsService;
this.bCryptPasswordEncoder = new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.cors().and().csrf().disable()
.authorizeRequests()
.antMatchers("/v2/api-docs", "/configuration/ui", "/swagger-resources", "/configuration/security", "/swagger-ui.html", "/webjars/**", "/swagger-resources/configuration/ui", "/swagger-resources/configuration/security").permitAll()
.anyRequest().authenticated()
.and()
.addFilter(new JwtAuthenticationFilter(authenticationManager()))
.addFilter(new JwtAuthorizationFilter(authenticationManager()))
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(bCryptPasswordEncoder);
}
@Bean
public CorsConfigurationSource corsConfigurationSource() {
final CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOrigins(Arrays.asList(BANK_API, INVENTORY_API, MARKET_API)); //TODO: is dit correct??
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "PATCH"));
configuration.setAllowCredentials(true);
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setExposedHeaders(Arrays.asList("X-Auth-Token","Authorization","Access-Control-Allow-Origin","Access-Control-Allow-Credentials"));
final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
return source;
}
}
My UserDetails服务方法实现:
@Service
public class AuthLogic implements UserDetailsService {
private HttpServletRequest request;
private IAccountRepository accountRepository;
private RestCallLogic restCall;
@Autowired
public AuthLogic(HttpServletRequest request, IAccountRepository accountRepository, RestCallLogic restCall){
this.request = request;
this.accountRepository = accountRepository;
this.restCall = restCall;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//get password
//make restcall to external login
}
}
在使用spring安全性实现时,有没有办法获取密码。因为我可以很容易地创建自己的类并从那里进行登录,但最好使用Spring安全性。此外,登录还返回一个令牌,我可以将其转换为用户。也许我只是想得太多了
为了进行API调用,我需要编写一个自定义AuthenticationProvider:
@Component
public class JwtAuthenticationProvider implements AuthenticationProvider {
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String username = authentication.getName();
String password = authentication.getCredentials().toString();
UserDetails principal = new User(username, password, new ArrayList<>());
return new UsernamePasswordAuthenticationToken(principal, password, new ArrayList<>());
}
@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
@组件
公共类JwtAuthenticationProvider实现AuthenticationProvider{
@凌驾
公共身份验证(身份验证)引发AuthenticationException{
字符串username=authentication.getName();
字符串密码=authentication.getCredentials().toString();
UserDetails principal=新用户(用户名、密码、新ArrayList());
返回新的UsernamePasswordAuthenticationToken(主体、密码、新ArrayList());
}
@凌驾
公共布尔支持(类身份验证){
返回authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
在幕后,Spring Security在筛选器中解析用户凭据(例如,BasicAuthenticationFilter
,UsernamePasswordAuthenticationFilter
等-筛选器检索用户凭据),如果此筛选器成功检索到用户的凭据,它会将此类凭据传递给AuthenticationProvider
,以验证凭据并创建用户详细信息()。AuthenticationProvider
可以通过各种方式验证凭据
AuthenticationProvider
的一个实现是DaoAuthenticationProvider
,它尝试在UserDetails服务
中按用户名查找用户,如果找到,则从UserDetails服务
获取用户的UserDetails
,然后检查用户提供的密码是否符合要求UserDetails
中的密码
在您的情况下,您需要在UserDetailsService
中提出此类请求,而不是在AuthenticationProvider
中,因为它对此类情况负责
我的建议是从spring security扩展AbstractUserDetailsAuthenticationProvider
类,并在抽象方法protected AbstractUserDetails retrieveUser(String username,UsernamePasswordAuthenticationTokenAuthentication)中实现您的功能抛出AuthenticationException代码>
例如:
@Configuration
public class WebSecurityConf43547 extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(new AbstractUserDetailsAuthenticationProvider() {
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken) throws AuthenticationException {
//from docs: "[...]Generally a subclass will at least compare the
//Authentication.getCredentials() with a UserDetails.getPassword() [...]"
}
@Override
protected UserDetails retrieveUser(String s, UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken) throws AuthenticationException {
usernamePasswordAuthenticationToken.getCredentials();
//your api here
}
});
}
}
更好的例子是:看看DaoAuthenticationProvider
如何在SpringSecurity中扩展AbstractUserDetailsAuthenticationProvider
。一周后,我终于得到了想要的。因此,我创建了一个自定义身份验证提供程序,它将对我的身份验证API进行REST调用。如果我提供的用户名和密码正确,我将获得一个包含用户名、角色和ID的JWT令牌。之后,我只需调用一个自定义身份验证服务,检查其数据库中是否已经存在该用户ID。如果不是这样,我将使用JWT令牌中的给定id创建一个新用户
这是我的自定义身份验证提供程序:
public class JwtAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
//custom authentication service
private AuthLogic userDetailsImpl;
public JwtAuthenticationProvider(AuthLogic userDetailsImpl) {
this.userDetailsImpl = userDetailsImpl;
}
@Override
protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
//JWTUser is a custom class that extends the UserDetails class from spring
JwtUser user = (JwtUser) userDetails;
//call the custom auth service to check if the user exists in the database
userDetailsImpl.loadUserByUsername(user.getUserID(), user.getUsername());
}
@Override
protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
//get the token from a external authentication API
String token = retrieveAccountData(new LoginWrapper(username, authentication.getCredentials().toString()));
Claims claims = Jwts.parser()
.setSigningKey(JWTKEY)
.parseClaimsJws(token)
.getBody();
List<String> scopes = (List<String>) claims.get("scopes");
int UserId = (int) claims.get("userID");
List<GrantedAuthority> authorities = scopes.stream()
.map(authority -> new SimpleGrantedAuthority(authority))
.collect(Collectors.toList());
//return the User
return new JwtUser(UserId, username, authentication.getCredentials().toString(), authorities);
}
private String retrieveAccountData(LoginWrapper loginWrapper){
URI uri = UriComponentsBuilder.fromUriString(BANK_LOGIN).build().toUri();
Gson gson = new GsonBuilder().create();
RequestEntity<String> request = RequestEntity
.post(uri)
.accept(MediaType.APPLICATION_JSON)
.body(gson.toJson(loginWrapper));
//post call
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<String> response = restTemplate.exchange(request, String.class);
//check if status code is correct
if(response.getStatusCode() != HttpStatus.OK) {
throw new UsernameNotFoundException(loginWrapper.getUsername());
}
//convert to LoginWrapper
return gson.fromJson(response.getBody(), TokenWrapper.class).getToken();
}
}
我希望这个答案有帮助。不,你不知道。。。您需要一个自定义的AuthenticationProvider
而不是自定义的UserDetailsService
。你试图在错误的地方解决它。谢谢你的有用信息!!!我知道的唯一问题是,我的自定义AuthenticationProvider在我执行/Login操作时不会触发,但您的AuthenticationProvider
没有调用任何东西?它只返回一个用户,根本不调用外部系统。另外,我怀疑您的JwtAuthenticationFilter
是否实际生成了UsernamePasswordAuthentication
。我是否必须在WebSecurity配置适配器中进行任何其他配置。因为我开始创建一个自定义AuthenticationProvider类,当我登录时它不会被触发,它只是说拒绝访问。还有一个问题:我应该在哪里调用数据库来检查本地数据库中是否存在该帐户?
@Service
public class AuthLogic {
private IAccountRepository accountRepository;
@Autowired
public AuthLogic(IAccountRepository context) {
this.accountRepository = context;
}
trough with the jwt token)
public UserDetails loadUserByUsername(int userId, String username) throws UsernameNotFoundException {
Optional<Account> foundAccount = accountRepository.findById(userId);
Account account;
//check if user has logged in to our inventory API before, if not create new account
if (!foundAccount.isPresent()) {
account = accountRepository.save(new Account(userId, username));
} else {
account = foundAccount.get();
}
return new JwtUserPrincipal(account);
}
}
@EnableWebSecurity
public class WebSecurity extends WebSecurityConfigurerAdapter {
private JwtAuthenticationProvider authenticationProvider;
@Autowired
public WebSecurity(@Qualifier("authLogic") AuthLogic userDetailsImpl) {
this.authenticationProvider = new JwtAuthenticationProvider(userDetailsImpl);
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
}