[英]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.