我正在开发一个 Spring Boot 1.4.1 应用程序(实现一些REST Web服务),我发现在尝试将 Spring Security 实现到这个项目时遇到了一些困难 .
我有以下情况:
1)我有实现Spring Security UserDetails 接口的 CustomUserDetails :
import com.betrivius.domain.User;
import com.betrivius.domain.UserRole;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
public class CustomUserDetails extends User implements UserDetails {
private static final long serialVersionUID = 1L;
public CustomUserDetails(User user){
super(user);
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
for(UserRole role : this.getUserRoles() ){
GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(role.getName());
authorities.add(grantedAuthority);
}
return authorities;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
@Override
public String getUsername() {
return super.getUsername();
}
}
2)然后我有 CustomUserDetailsService 实现Spring Security UserDetailsService 接口:
import com.betrivius.dao.UserDAO;
import com.betrivius.domain.User;
import com.betrivius.security.bean.CustomUserDetails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Transactional
@Service("customUserDetailsService")
public class CustomUserDetailsService implements UserDetailsService{
@Autowired
private UserDAO userDao;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userDao.findByUsername(username);
if(user == null){
throw new UsernameNotFoundException("No user present with username: "+username);
}else{
return new CustomUserDetails(user);
}
}
}
3)最后我有一个名为 WebSecurityConfig 的Spring Security confinguration类,扩展了 WebSecurityConfigurerAdapter :
import com.betrivius.security.service.CustomUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = CustomUserDetailsService.class)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
public void configAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordencoder());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/Accomodation/**").access("hasRole('ROLE_USER')")
.anyRequest().permitAll()
.and()
.csrf().disable();
/*.and()
.formLogin().loginPage("/login")
.usernameParameter("username").passwordParameter("password")
.and()
.logout().logoutSuccessUrl("/login?logout")
.and()
.exceptionHandling().accessDeniedPage("/403")
.and()
.csrf();*/
}
@Bean(name = "passwordEncoder")
public PasswordEncoder passwordencoder() {
return new BCryptPasswordEncoder();
}
}
正如您在配置类中的前一个代码片段中所看到的,我已经指定只有设置了 ROLE_USER 的用户才能访问 /Accomodation/ 路径下的所有资源,我通过以下方式完成了:
.antMatchers("/Accomodation/**").access("hasRole('ROLE_USER')")
4)这是 UserDAO ,它获取用于前一个 CustomUserDetailsService 类的用户信息:
@Repository
@Transactional(propagation = Propagation.MANDATORY)
public interface UserDAO extends JpaRepository<User, Long> {
User findByUsername(String username);
@Query("SELECT r FROM User u JOIN u.userRoles r where u.username = :username")
List<UserRole> findRoleByUserName(String username);
}
用户名和密码在数据库中是stord,并且是:
USERNAME:ErSabba
密码:pswd
User 表与 User_Roles 有多对多的关系, ErSabba 用户的作用是 ROLE_USER (尝试设置 USER 作为角色) .
因此,通过 PostMan Chrome插件,我会对网址执行GET请求:
http://localhost:8080/Accomodation/7
以这种方式传递以前的凭证:
正如您所看到的,问题是将正确的用户名和密码插入 basic authentication form 它会给我 403 error Access denied .
我认为这意味着我已经执行了身份验证,但我没有授权 .
奇怪的是,我也试图把坏凭证,我总是得到相同而不是 401 Bad Credential 错误 .
我还在 loadUserByUsername() 方法的这一行上放了一个断点到 CustomUserDetailsService 类(检索 User 信息) .
User user = userDao.findByUsername(username);
在调试模式下运行它不会在此行停止,所以似乎 Spring Security 配置不能正常工作...
可能是什么问题呢?我错过了什么?我该如何解决这个问题?
1 回答
我建议你在没有数据库的情况下进行测试 . 您可以提供
inMemory
配置,如下所示:然后尝试登录 . 如果它可以工作,它将显示DB中的凭据有问题 .
如何在DB中存储密码?如果它是纯文本,它将无法正常工作,因为您有密码编码器,它应该将您的密码读作哈希 .
希望有所帮助 .