簡體   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