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