角色在 Spring 安全性中无法正常工作 [重复]

Posted

技术标签:

【中文标题】角色在 Spring 安全性中无法正常工作 [重复]【英文标题】:Roles not working properly in Spring security [duplicate] 【发布时间】:2019-11-15 08:09:36 【问题描述】:

我正在开发一个基于 Spring Security 的 Web 应用程序,我想根据登录用户是否具有角色 ADMIN 或 USER 来限制侧栏上的项目。就身份验证而言,一切正常,但角色未按预期工作。

Following this post

例如——

<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);
        
    

【问题讨论】:

【参考方案1】:

这看起来信息在从数据库检索到向浏览器返回响应之间的某个地方丢失了(显然)。幸好你有一个调试器,所以我会从跟踪你访问数据库数据的每一步开始,直到你返回该信息。跟踪每一步,直到找到丢失的位置,并在将其缩小到一个地方后发回。如果不是,也许我们可以开始查看您的 html/脚本/模板引擎,但前提是我们确定信息已到达浏览器。

【讨论】:

以上是关于角色在 Spring 安全性中无法正常工作 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

基于 Spring Boot / Spring Security 角色的授权无法正常工作 [重复]

Spring Security 中的 hasRole() 无法正常工作 [重复]

Thymeleaf和Spring安全无法正常工作[重复]

Spring 安全配置无法正常工作

Spring Security,Boot:安全规则无法正常工作

如何使用 Spring 安全性从 Active Directory LDAP 填充 LDAP 权限?