我正在使用JWT身份验证进行Spring Security实现。我不确定如何在方法级别检查用户角色。我在互联网上看到了这个示例:
@PostMapping("{id}")
@Secured({"ROLE_ADMIN"})
public ResponseEntity<?> save(@PathVariable Integer id, @RequestBody UserNewDTO dto) {
........
}
我是否需要从JWT令牌中提取用户类型?是否有其他方法可以实现此目的?在我看来,仅使用@Secured({"ROLE_ADMIN"})
并不完整。
答案 0 :(得分:1)
有很多方法可以使用注释以及基于端点的安全配置来设计基于权限的 API 访问。
注释:
@Secured
@PreAuthorize
@PostAuthorize
@RolesAllowed
@PreFilter
@PostFilter
为了使用注解,您需要启用如下安全配置
@Configuration
@EnableGlobalMethodSecurity(
prePostEnabled = true,
securedEnabled = true,
jsr250Enabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {
}
pre/post
注释@Secured
注释@RoleAllowed
注释@Secured & @RoleAllowed
具有给定角色的用户能够执行该方法。 @RoleAllowed
注释是 JSR-250 中 @Secured
注释的等效注释。
@Secured({ "ROLE_ADMIN", "ROLE_SUPERADMIN" })
public ResponseEntity<?> save(...) {
...
}
@RolesAllowed({ "ROLE_ADMIN", "ROLE_SUPERADMIN" })
public ResponseEntity<?> save(...) {
...
}
@PreAuthorize & @PostAuthorize
@PreAuthorize
批注在进入方法之前检查给定的表达式,而 @PostAuthorize
批注在方法执行后对其进行验证并可能改变结果。
@PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_SUPERADMIN')")
public ResponseEntity<?> save(...) {
...
}
@PreAuthorize & @PostAuthorize
和 @Secured
的主要区别在于 @Secured
不支持 SpEL(Spring 表达式语言)。要查看更多差异,您可以阅读更多详细信息here
@PreAuthorize("#username == authentication.principal.username")
public String methodX(String username) {
//...
}
@PostAuthorize("#username == authentication.principal.username")
public String methodY(String username) {
//...
}
这里,只有当参数 username 的值与当前主体的用户名相同时,用户才能调用 methodX。您可以检查其他可能的 SpEL(Spring 表达式语言) 自定义 here
您可以从here
获得更多详细信息使用 configure(HttpSecurity http)
和 configure(WebSecurity web)
方法。
@EnableWebSecurity
@EnableGlobalMethodSecurity(
prePostEnabled = true,
securedEnabled = true,
jsr250Enabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) {
web
.ignoring()
.antMatchers("/app/**/*.{js,html}")
.antMatchers("/i18n/**")
.antMatchers("/content/**")
.antMatchers("/swagger-ui/**")
.antMatchers("/test/**");
}
@Override
public void configure(HttpSecurity http) throws Exception {
// @formatter:off
http
.csrf()
.disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/public/**").permitAll()
.antMatchers("/api/**").hasAuthority(AuthoritiesConstants.USER)
.antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN);
// @formatter:on
}
}
configure(WebSecurity web)
此方法中使用的端点会忽略 spring 安全过滤器,也忽略安全功能(安全标头、csrf 保护等),并且不会设置安全上下文并且无法保护端点以进行跨站点脚本、XSS 攻击、内容嗅探。< /p>
configure(HttpSecurity http)
此方法中使用的端点会忽略 antMatchers 中使用的端点的身份验证,其他安全功能将生效,例如安全标头、CSRF 保护等。
您可以将 hasRole()、hasAnyRole()、hasAuthority()、hasAnyAuthority() 方法与 configure(HttpSecurity http)
一起使用。请注意,对于 hasRole() 和 hasAnyRole() 方法,您不需要使用 ROLE_ 前缀,而对于其他两个方法,您必须使用 ROLE_
要了解区别和用法,您可以获取详细信息here
您还可以按如下方式创建 utils 方法,这可能会有所帮助。
/**
* Get the login of the current user.
*
* @return the login of the current user.
*/
public static Optional<String> getCurrentUserLogin() {
SecurityContext securityContext = SecurityContextHolder.getContext();
return Optional.ofNullable(securityContext.getAuthentication())
.map(authentication -> {
if (authentication.getPrincipal() instanceof UserDetails) {
UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal();
return springSecurityUser.getUsername();
} else if (authentication.getPrincipal() instanceof String) {
return (String) authentication.getPrincipal();
}
return null;
});
}
/**
* Check if a user is authenticated.
*
* @return true if the user is authenticated, false otherwise.
*/
public static boolean isAuthenticated() {
SecurityContext securityContext = SecurityContextHolder.getContext();
return Optional.ofNullable(securityContext.getAuthentication())
.map(authentication -> {
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.addAll(authentication.getAuthorities());
return authorities.stream()
.noneMatch(grantedAuthority -> grantedAuthority.getAuthority().equals(AuthoritiesConstants.ANONYMOUS));
})
.orElse(false);
}
/**
* If the current user has a specific authority (security role).
* <p>
* The name of this method comes from the {@code isUserInRole()} method in the Servlet API.
*
* @param authority the authority to check.
* @return true if the current user has the authority, false otherwise.
*/
public static boolean isCurrentUserInRole(String authority) {
SecurityContext securityContext = SecurityContextHolder.getContext();
return Optional.ofNullable(securityContext.getAuthentication())
.map(authentication -> {
List<GrantedAuthority> authorities = new ArrayList<>();
authorities.addAll(authentication.getAuthorities());
return authorities.stream()
.anyMatch(grantedAuthority -> grantedAuthority.getAuthority().equals(authority));
})
.orElse(false);
}
public static Optional<Authentication> getAuthenticatedCurrentUser() {
log.debug("Request to get authentication for current user");
SecurityContext securityContext = SecurityContextHolder.getContext();
return Optional.ofNullable(securityContext.getAuthentication());
}
答案 1 :(得分:1)
我主要在我的 Web 应用程序中同时使用 JWT 身份验证和 spring 安全性。以下是我的常见做法:
private Claims getClaimsFromToken(String token, String key) throws ServletException {
return Jwts.parser()
.setSigningKey(key)
.parseClaimsJws(token)
.getBody();
User user = getUserFromToken(token);
List<GrantedAuthority> authorities = getGrantedAuthorities(user);
public List<GrantedAuthority> getGrantedAuthorities(User user) {
List<GrantedAuthority> result = new ArrayList<>();
for (String privilegeName : user.getAuthorities()){ // e.g. ["READ", "WRITE"]
result.add(new SimpleGrantedAuthority(privilegeName));
}
return result;
}
org.springframework.security.authentication.AbstractAuthenticationToken
与您的用户及其权限联系并注入 SecurityContextHolder
。AuthenticationFilter.java:
JWTAuthenticationToken jwtAuthenticationToken = new JWTAuthenticationToken(user,
authorities);
JWTAuthenticationToken.java
public class JWTAuthenticationToken extends AbstractAuthenticationToken {
private User user;
public JWTAuthenticationToken(User user, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.user = user;
}
@PreAuthorize
用户可以访问。@PreAuthorize("hasAnyAuthority('READ')")
SecurityContextHolder
获取用户。User User= SecurityContextHolder.getContext().getAuthentication().getUser();
答案 2 :(得分:0)
您尝试过吗:
@PreAuthorize ("hasRole('ROLE_ADMIN')")
编辑: 要检查用户是否被分配了多个角色,请使用:
@PreAuthorize("hasAnyRole('ROLE_ADMIN','ROLE_MANAGER')")
答案 3 :(得分:0)
此link解释了有关JWT身份验证的所有内容。 在下面,您可以看到一些示例可以用作修改代码的基础:
@CrossOrigin(origins = "*", maxAge = 3600)
@RestController
@RequestMapping("/api/test")
public class TestController {
@GetMapping("/all")
public String allAccess() {
return "Public Content.";
}
@GetMapping("/user")
@PreAuthorize("hasRole('USER') or hasRole('MODERATOR') or hasRole('ADMIN')")
public String userAccess() {
return "User Content.";
}
@GetMapping("/mod")
@PreAuthorize("hasRole('MODERATOR')")
public String moderatorAccess() {
return "Moderator Board.";
}
@GetMapping("/admin")
@PreAuthorize("hasRole('ADMIN')")
public String adminAccess() {
return "Admin Board.";
}
}
答案 4 :(得分:0)
您可以实现自己的 AbstractPreAuthenticatedProcessingFilter
并自己创建您的 principal
。
@Override
protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
final String token = request.getHeader("YOUR_HEADER");
DecodedJWT jwt = JWT.decode(token);
// TODO create principal
}