繁体   English   中英

Spring 安全与 JWT 角色不工作

[英]Spring Security with JWT Roles not working

我正在使用 Spring 引导和 Java 14。

我正在尝试使基于角色的身份验证正常工作。 我正在使用 JWT。 我可以让身份验证工作,即我得到 JWT 并将该令牌成功用于所有未来的请求。

但是,我无法让基于角色的权限正常工作,即如果安全配置尝试将端点与角色匹配,我总是会得到 403。

例如,如果我使用有效的 jwt 发送以下请求,其中用户确实具有正确的角色( 'approver admin' ):

GET http://localhost:8080/approvals

Header:  Bearer eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJyaWNoYXJkbWFyYWlzIiwiZXhwIjoxNTkyODgwNzUzLCJpYXQiOjE1OTI4NDQ3NTN9.MgiH5--a4U8phKz_jmjmBuxRt8iqhhcHHnxrhxGOQqM

我得到以下回复:

{
    "timestamp": "2020-06-22T16:56:22.113+00:00",
    "status": 403,
    "error": "Forbidden",
    "message": "Forbidden",
    "path": "/approvals"
}

我的代码:

安全配置.java

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    @Qualifier("datasource1")
    private DataSource dataSource;

    @Autowired
    private JwtRequestFilter jwtRequestFilter;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //auth.userDetailsService(approvalUserDetailsService);
        auth.jdbcAuthentication().dataSource(dataSource)
        .usersByUsernameQuery("SELECT username, password, (NOT disabled) as enabled FROM members "+
                "WHERE username = ?")
        .authoritiesByUsernameQuery("SELECT m.username, t.name as authority FROM members m " +
                "JOIN administrator a ON a.member_id = m.member_id " +
                "JOIN admin_type t ON t.admin_type_id = a.admin_type_id "+
                "WHERE m.username = ?");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // set up the jwt auth
        http.cors().disable();
        http.csrf().disable().authorizeRequests().antMatchers("/authenticate").permitAll()//.anyRequest().authenticated()
                .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
                .and().exceptionHandling()
                .and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); // don't manage sessions, using jwt
        http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);

        // define the role mappings
        http.authorizeRequests()
                .antMatchers("/admin").hasRole("approver admin")
                .antMatchers("/approvals").hasRole("approver admin")
                .antMatchers("/hello").permitAll();
    }

JwtRequestFilter.java

@Component
public class JwtRequestFilter extends OncePerRequestFilter {

    @Autowired
    private ApprovalUserDetailsService approvalUserDetailsService;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        final String authorizationHeader = request.getHeader("Authorization");
        String username = null;
        String jwt = null;

        if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
            jwt = authorizationHeader.substring(7);
            username = jwtTokenUtil.extractUserName(jwt);
        }
        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails = this.approvalUserDetailsService.loadUserByUsername(username);
            if (jwtTokenUtil.validteToken(jwt, userDetails)) {
                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
            }
        }
        filterChain.doFilter(request, response);
    }
}

ApprovalUserDetailsService.java

@Service
public class ApprovalUserDetailsService implements UserDetailsService {

    @Autowired
    NamedParameterJdbcTemplate namedParameterJdbcTemplate;

    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
        String sql = "SELECT username, password FROM members WHERE username = :userName";
//        String sql = "SELECT m.username, m.password, t.name as authority FROM members m " +
//                "JOIN administrator a ON a.member_id = m.member_id " +
//                "JOIN admin_type t ON t.admin_type_id = a.admin_type_id " +
//                "WHERE m.username = :userName";
        SqlParameterSource namedParameters = new MapSqlParameterSource().addValue("userName", userName);
        User users = namedParameterJdbcTemplate.queryForObject(sql, namedParameters, new UserDetailsRowMapper());
        return users;
        //        return new User("foo", "foo", new ArrayList<>());
    }

    class UserDetailsRowMapper implements RowMapper<User> {
        @Override
        public User mapRow(ResultSet rs, int rowNum) throws SQLException {
            List<? extends GrantedAuthority> roles = new ArrayList<>();
            //String authority = rs.getString("authority");
            User user = new User(rs.getString("username"), rs.getString("password"), roles);
            return user;
        }
    }
}

ApprovalUserDetailsService中, List<? extends GrantedAuthority> roles = new ArrayList<>(); List<? extends GrantedAuthority> roles = new ArrayList<>(); 只是一个空列表。 这是否需要填充“审批者管理员”角色? 那是我的问题吗?

我之所以将其留空,是因为我遵循的教程将其留空。 我认为过滤器可能仅用于获取 jwt,而不用于角色验证,因此不需要。 还是我不正确并且需要填充?

如果认为SecurityConfig.java中的查询是获取要验证的角色。 IE

    .authoritiesByUsernameQuery("SELECT m.username, t.name as authority FROM members m " +
            "JOIN administrator a ON a.member_id = m.member_id " +
            "JOIN admin_type t ON t.admin_type_id = a.admin_type_id "+
            "WHERE m.username = ?");

在此处输入图像描述

在 ApprovalUserDetailsService 中,List<? 扩展 GrantedAuthority> 角色 = 新 ArrayList<>(); 只是一个空列表。 这是否需要填充“审批者管理员”角色? 那是我的问题吗?

是的。 它需要填充“批准者管理员”。 您可以使用AuthorityUtils将字符串角色名称转换为GrantedAuthority

    class UserDetailsRowMapper implements RowMapper<User> {
        @Override
        public User mapRow(ResultSet rs, int rowNum) throws SQLException {
            String authority = rs.getString("authority");
            User user = new User(rs.getString("username"), rs.getString("password"), AuthorityUtils.createAuthorityList(authority););
            return user;
        }
    }

同样hasRole("approver admin")期望用户具有ROLE_approver admin角色, ROLE_前缀将自动添加。 更改为使用hasAuthority()进行完全匹配而不添加任何ROLE_前缀:

  http.authorizeRequests()
        .antMatchers("/admin").hasAuthority("approver admin")
        .antMatchers("/approvals").hasAuthority("approver admin")

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM