spring - 角色在 Spring 安全性中无法正常工作
问题描述
我正在开发一个基于 Spring Security 的 Web 应用程序,在该应用程序中,我想根据登录用户是否具有角色 ADMIN 或 USER 来限制侧栏上的项目。就身份验证而言,一切正常,但角色未按预期工作。
例如 -
<security:authorize access="hasRole('ADMIN')">
<li class=" nav-item"><a href="<c:url value = "/mapview/list"/>"><i
class="fa fa-map-marker"></i><span class="menu-title" data-i18n="">MapView</span></a>
</li>
</security:authorize>
即使我以 ADMIN 身份登录,上述元素也永远不可见。
有人可以在这里帮助我了解发生了什么问题。
安全配置
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
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.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class AppSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsService userDetailsService;
@Bean
public PasswordEncoder passwordEncoder() {
PasswordEncoder encoder = new BCryptPasswordEncoder();
return encoder;
}
@Bean
public AuthenticationProvider authProvider() {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(new BCryptPasswordEncoder());
return provider;
}
@Override
protected void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.authorizeRequests().antMatchers("/login", "/resource/**").permitAll()
.anyRequest().fullyAuthenticated()
.and()
.formLogin()
.loginPage("/login").usernameParameter("username").passwordParameter("password").permitAll()
.loginProcessingUrl("/doLogin").successForwardUrl("/postLogin").failureUrl("/loginFailed").and()
.logout().logoutUrl("/doLogout").logoutSuccessUrl("/logout").permitAll().and().csrf().disable();
}
}
UserDetailsImpl
public class UserDetailsImpl implements UserDetails {
private static final long serialVersionUID = 1L;
private Collection<SimpleGrantedAuthority> authorities;
private String username;
private String password;
private Boolean enabled = true;
public void setAuthorities(Collection<SimpleGrantedAuthority> authorities) {
this.authorities = authorities;
}
public void setUsername(String username) {
this.username = username;
}
public void setPassword(String password) {
this.password = password;
}
public void setEnabled(Boolean enabled) {
this.enabled = enabled;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return enabled;
}
用户详情服务
@Service("userDetailsService")
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private UserService userService;
private Converter<User, UserDetails> userUserDetailsConverter;
@Autowired
@Qualifier(value = "userToUserDetails")
public void setUserUserDetailsConverter(Converter<User, UserDetails> userUserDetailsConverter) {
this.userUserDetailsConverter = userUserDetailsConverter;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userUserDetailsConverter.convert(userService.findByUserName(username));
}
}
用户到用户详细信息
@Component
public class UserToUserDetails implements Converter<User, UserDetails>{
@Override
public UserDetails convert(User user) {
UserDetailsImpl userDetails = new UserDetailsImpl();
if (user != null) {
userDetails.setUsername(user.getUsername());
userDetails.setPassword(user.getEncryptedPassword());
userDetails.setEnabled(user.getEnabled());
Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
user.getRoles().forEach(role -> {
authorities.add(new SimpleGrantedAuthority(role.getName()));
});
userDetails.setAuthorities(authorities);
}
return userDetails;
}
}
控制器
@SessionAttributes({ "currentUser" })
@Controller
public class HomeController {
//skipping other mappings
@RequestMapping(value = "/postLogin", method = RequestMethod.POST)
public String postLogin(Model model, HttpSession session) {
UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) SecurityContextHolder
.getContext().getAuthentication();
validatePrinciple(authentication.getPrincipal());
String username = ((UserDetailsImpl) authentication.getPrincipal()).getUsername();
model.addAttribute("currentUser", username);
return "redirect:/dashboard";
}
}
用户
@Entity
public class User extends Auditable<String> {
//skipping other details
@ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@JoinTable(name = "user_role",
joinColumns = { @JoinColumn(name = "user_id") },
inverseJoinColumns = { @JoinColumn(name = "role_id") })
private Set<Role> roles = new HashSet<>();
}
在 db- 中创建的表
user
role
user_role
编辑 1
刚刚在我的控制器中添加了这个映射,一旦我登录,如果我在浏览器上点击 /test,两个布尔值都显示false。我不知道为什么没有设置角色.. :-(
@GetMapping(value = "/test")
public void test(SecurityContextHolderAwareRequestWrapper request) {
boolean b = request.isUserInRole("ADMIN");
System.out.println("ROLE_ADMIN=" + b);
boolean c = request.isUserInRole("USER");
System.out.println("ROLE_USER=" + c);
}
编辑 2
但是,同时,下面的代码显示角色为 ADMIN
public void test(SecurityContextHolderAwareRequestWrapper request) {
for (GrantedAuthority authority : SecurityContextHolder.getContext().getAuthentication().getAuthorities()) {
String userRole = authority.getAuthority();
System.out.println(userRole);
}
}
解决方案
这看起来像是在从数据库检索和向浏览器返回响应之间的某个地方丢失了信息(显然)。幸好你有一个调试器,所以我会从跟踪你访问数据库数据的每一步开始,直到你返回该信息。跟踪每一步,直到找到丢失的位置,并在将其缩小到一个地方后发回。如果不是,也许我们可以开始查看您的 html/脚本/模板引擎,但只有在我们确定信息到达浏览器之后。
推荐阅读
- sql - JIRA JQL 按日期搜索 - 今天有没有办法排除
- c++ - 为什么简单的乘法会导致一些乱码?
- javascript - 如何根据其他请求处理请求或离开页面?
- memory-management - OOM 内核恐慌内存总结不正确
- python - 如何在pandas df中获取与其名称相关的列数
- python-3.x - 如何使用 df.to_csv 为多索引数据框 python3 格式化 csv 文件
- javascript - How to make a progress bar increase in size by increasing the width but without any inline style so it doesn't upset CSP
- c++ - 如何从向量中删除重复项
> - xamarin - Error when changing the style to dark from the system
- lambda - F# 表达式仅在泛型内自动从函数转换?