これはSpringでの最初のプロジェクトであり、SpringSecurityでログインを作成し始めたところです。一部のページには、管理者のみがアクセスでき、プレーヤーはアクセスできないようにします。Webでいくつかの例を見つけましたが、このメカニズムは非常にうまく機能します。ログインによって保護されているこの保護されたページがあり、ユーザーにROLE_ADMINがない場合は禁止されています。
@PreAuthorize("hasAuthority('ROLE_ADMIN')")
@GetMapping("/secured/all")
public String securedHello() {
return "Secured Hello";
}
問題は、コードをテストしたところ、Springが管理者(およびユーザーも)を認証するのはユーザー名のみをチェックしていることがわかったということです。間違ったパスワードを入力すると、とにかく入力できます。これがどのように可能かわかりませんが、Spring Securityはすべての認証を単独で機能させるべきではありませんか?誰かが認証マネージャーなどを実装するよう提案しているのを見たことがありますが、それをコードに挿入する理由と方法がわかりません。私は2日以来これに固執しています、どんなアドバイスでも本当に感謝します。これらは私のクラスです:
package model;
import java.io.IOException;
import javax.naming.AuthenticationException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.http.HttpStatus;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.PasswordEncoder;
import com.fasterxml.jackson.databind.ObjectMapper;
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
@EnableJpaRepositories(basePackageClasses = PlayersRepository.class)
@ComponentScan(basePackageClasses= CustomUserDetailsService.class)
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Autowired
private CustomUserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(getPasswordEncoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//http.csrf().disable();
http.authorizeRequests()
.antMatchers("**/secured/**").access("hasAuthority('ROLE_ADMIN')")
.anyRequest().permitAll()
.and()
.formLogin().permitAll();
}
private PasswordEncoder getPasswordEncoder() {
return new PasswordEncoder() {
@Override
public String encode(CharSequence charSequence) {
return charSequence.toString();
}
@Override
public boolean matches(CharSequence charSequence, String s) {
return true;
}
};
}
}
package model;
import java.util.ArrayList;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
public class CustomUserDetailsService implements UserDetailsService {
@Autowired
private PlayersRepository usersRepository;
@Autowired
private RoleRepository rolesRepository;
public CustomUserDetailsService(PlayersRepository usersRepository, RoleRepository rolesRepository) {
this.usersRepository=usersRepository;
this.rolesRepository=rolesRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
Optional<Player> optionalUser = usersRepository.findByUsername(username);
optionalUser
.orElseThrow(() -> new UsernameNotFoundException("Username not found"));
Player user= optionalUser.get();
System.out.println(user);
return toUserDetails(new UserObject(user.getUsername(),user.getPassword(),user.getRole()));
}
private UserDetails toUserDetails(UserObject userObject) {
return User.withUsername(userObject.name)
.password(userObject.password)
.roles(userObject.role).build();
}
private static class UserObject {
private String name;
private String password;
private String role;
public UserObject(String name, String password, String role) {
this.name = name;
this.password = password;
this.role = role;
}
}
}
package model;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
public class CustomUserDetails extends Player implements UserDetails {
String role;
public CustomUserDetails(final Player user) {
super(user);
}
public CustomUserDetails(Optional<Player> user, String role) {
super(user);
this.role=role;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> list = new ArrayList<GrantedAuthority>();
list.add(new SimpleGrantedAuthority("ROLE_"+ role));
System.out.println(list);
return list;
}
@Override
public String getPassword() {
return super.getPassword();
}
@Override
public String getUsername() {
return super.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
Spring Securityはすべての認証を単独で機能させるべきではありませんか?
はい、SpringSecurityはを使用してそれを行いますAuthenticationManager
。
誰かが認証マネージャーなどを実装するよう提案しているのを見たことがありますが、それをコードに挿入する理由と方法がわかりません。
メソッドAuthenticationManager
内でビルドしたので、実際にはすでにがありますconfigure()
。
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(getPasswordEncoder());
}
だから、これが機能しない正確な理由は何ですか?さて、AuthenticationManager
あなたが提供したものには2つの部分が含まれています。
CustomUserDetailsService
)getPasswordEncoder()
)。画面の背後で発生するのは、SpringがあなたCustomUserDetailsService
に電話をかけて、(ハッシュされた)パスワードを含むユーザー情報を取得することです。その情報を取得した後、PasswordEncoder.matches()
関数を呼び出して、入力された生のパスワードが、によって提供されたハッシュされたパスワードと一致するかどうかを確認しますCustomUserDetailsService
。
あなたの場合、PasswordEncoder.matches()
関数は次のようになります。
@Override
public boolean matches(CharSequence charSequence, String s) {
return true;
}
これは、指定したパスワードに関係なく、を返すことを意味しますtrue
。どのパスワードでも機能するので、これはまさにあなたが経験していることです。
では、これをどのように解決しますか?さて、あなたPasswordEncoder
は実際にあなたの生のパスワードをハッシュし、それを渡されているハッシュされたパスワードと比較するべきです、例えば:
@Override
public boolean matches(CharSequence rawPassword, String hashedPassword) {
String hashedPassword2 = null; // hash your rawPassword here
return hashedPassword2.equals(hashedPassword);
}
このメソッドの実装は、データベースにパスワードを保存する方法によって異なります。春のセキュリティはすでに含めて、いくつかの実装が付属していますBcryptPasswordEncoder
、StandardPasswordEncoder
、MessageDigestPasswordEncoder
、...。これらの実装の一部は非推奨です。これは主に、これらのエンコーダーで使用されるハッシュメカニズムが安全でないと見なされていることを示すためです。Javadocで言及されているように、現時点では、これらのエンコーダーを削除する予定はありません。
ダイジェストベースのパスワードエンコーディングは安全とは見なされません。代わりのような適応1の一方向関数を使用し
BCryptPasswordEncoder
、Pbkdf2PasswordEncoder
またはSCryptPasswordEncoder
。DelegatingPasswordEncoder
パスワードのアップグレードをサポートするさらに良い使用法。このサポートを削除する予定はありません。これがレガシー実装であり、それを使用することは安全でないと見なされることを示すことは非推奨です。
(強調は私自身のものです)
選択する実装を自由に選択できる場合はBCryptPasswordEncoder
、Javadocで説明されているようにSpringを使用することをお勧めします。
パスワードをエンコードするためのサービスインターフェイス。推奨される実装は
BCryptPasswordEncoder
です。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加