[英]Role Based Security Based on Jsonwebtoken using different roles for different controllers in Spring Boot
I am successfully able to authenticate users, acquire a jsonwebtoken, and access protected urls. 我能够成功验证用户身份,获取jsonwebtoken并访问受保护的网址。 I am persisting a claim to the token with a role of "user" or "admin". 我坚持以“用户”或“管理员”的角色对令牌提出要求。 What I would ideally, like to be able to do, after verifying the user, is persist the role based on the user role/roles, and protect specific url's in my api, based on those. 理想情况下,我希望能够在验证用户之后执行基于用户角色/角色的角色持久化操作,并基于这些角色保护我api中的特定URL。 I have the following setup. 我有以下设置。 How do I apply security to a specific url to differentiate based on role in the token? 如何将安全性应用于特定的URL,以根据令牌中的角色进行区分?
Package com.vicentex.api;
// imports excluded for brevity
@SpringBootApplication
public class VicentexTradingApiApplication {
@Bean
public FilterRegistrationBean jwtFilter() {
final FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new JwtFilter());
registrationBean.addUrlPatterns("/api/*");
return registrationBean;
}
public static void main(String[] args) {
SpringApplication.run(VicentexTradingApiApplication.class, args);
}
}
/////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////
@RestController
public class UserController {
@Autowired
private UserService userService;
@RequestMapping(value = "/api/users", method = RequestMethod.GET)
List<User> getAllUsers(){
return this.userService.getAllUsers();
}
@RequestMapping(value = "/api/users/{userName}",method = RequestMethod.GET)
User getUser(@PathVariable String userName) {
return this.userService.getUser(userName);
}
@RequestMapping(value = "/api/users", method = RequestMethod.POST)
void addUser(@RequestBody User user) {
this.userService.addUser(user);
}
@RequestMapping(value = "/api/users", method = RequestMethod.PUT)
void updateUser(@RequestBody User user) {
this.userService.updateUser(user);
}
@RequestMapping(value = "/api/users/{userName}", method = RequestMethod.DELETE)
void deleteUser(@PathVariable String userName) {
this.userService.deleteUser(userName);
}
@RequestMapping(value = "/register", method = RequestMethod.POST)
public User registerUser(@RequestBody User user) {
userService.addUser(user);
return user;
}
@RequestMapping(value = "/login", method = RequestMethod.POST)
public String login(@RequestBody User login) throws ServletException {
System.out.println("Controller Secret: " + Constants.secretKey);
String jwtToken = "";
if (login.getUserName() == null || login.getPassword() == null) {
throw new ServletException("Please fill in username and password");
}
String userName = login.getUserName();
String password = login.getPassword();
User user = userService.Authenticate(userName, password);
if (user == null) {
throw new ServletException("User not found.");
}
String pwd = user.getPassword();
if (!password.equals(pwd)) {
throw new ServletException("Invalid login. Please check your name and password.");
}
if (user.isAdmin()) {
jwtToken = Jwts.builder()
.setSubject(userName)
.claim("roles", "admin")
.setIssuedAt(new Date())
.signWith(SignatureAlgorithm.HS256, Constants.secretKey)
.compact();
} else {
jwtToken = Jwts.builder()
.setSubject(userName)
.claim("roles", "user")
.setIssuedAt(new Date())
.signWith(SignatureAlgorithm.HS256, Constants.secretKey)
.compact();
}
return jwtToken;
}
}
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
package com.vicentex.api.models;
@Document(collection = "Users")
public class User {
@Id
private ObjectId id;
private String userName;
private String fullName;
private String email;
private String password;
private String image;
private boolean isAdmin = false; //flagged for simplicity. Will be an array
Account acct;
@DBRef
List<Transaction> transactions;
public User() {
super();
}
public User(String userName, String fullName, String email, String password, String image) {
super();
this.userName = userName;
this.fullName = fullName;
this.email = email;
this.password = password; //note to remember to encrypt on creation
this.image = image;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getFullName() {
return fullName;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
public boolean isAdmin() {
return isAdmin;
}
public void setAdmin(boolean isAdmin) {
this.isAdmin = isAdmin;
}
public Account getAcct() {
return acct;
}
public void setAcct(Account acct) {
this.acct = acct;
}
public List<Transaction> getTransactions() {
return transactions;
}
public void setTransactions(List<Transaction> transactions) {
this.transactions = transactions;
}
}
In Spring you will use @PreAuthorize( "hasRole('somrole')" )
annotation on the method level. 在Spring中,您将在方法级别使用@PreAuthorize( "hasRole('somrole')" )
批注。 Spring is expecting the role to be in "authorities" claim. Spring预计该角色将出现在“权威”声明中。
In order to utilize @PreAuthorize, I had to include Spring Security as a dependency in my pom.xml. 为了利用@PreAuthorize,我必须将Spring Security作为依赖项包含在pom.xml中。 This forced me to have a password for the API, which isn't what I wanted. 这迫使我输入API的密码,这不是我想要的。 Worse, if I disabled the password, it basically defeated the purpose, as nothing else worked. 更糟糕的是,如果我禁用了密码,则根本无法达到目的,因为没有其他方法可以起作用。 I simply wanted to utilize the jsonwebtoken as a security mechanism over https, and protect a specific subset of url's based on roles in the token. 我只是想利用jsonwebtoken作为https上的安全机制,并根据令牌中的角色来保护url的特定子集。 So, what I actually ended up doing was this: 因此,我实际上最终要做的是:
public class JwtFilter extends GenericFilterBean {
public void doFilter(final ServletRequest req, final ServletResponse res, final FilterChain chain)
throws IOException, ServletException {
System.out.println("Secret Key: " + Constants.secretKey);
final HttpServletRequest request = (HttpServletRequest) req;
final HttpServletResponse response = (HttpServletResponse) res;
final String authHeader = request.getHeader("authorization");
if ("OPTIONS".equals(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
chain.doFilter(req, res);
} else {
if (authHeader == null || !authHeader.startsWith("Bearer ")) {
throw new ServletException("Missing or invalid Authorization header");
}
final String token = authHeader.substring(7);
try {
final Claims claims = Jwts.parser().setSigningKey(Constants.secretKey).parseClaimsJws(token).getBody();
String[] roles = claims.get("roles").toString().split(",");
if (request.getRequestURI().contains("/api/")) {
if (!Arrays.asList(roles).contains("user")) {
System.out.println("ROLES: " + claims.get("roles"));
throw new ServletException("A minimum security credential of 'user' is required to access this resource!");
}
}
if (request.getRequestURI().contains("/api/admin")) {
if (!Arrays.asList(roles).contains("admin")) {
System.out.println("ROLES: " + claims.get("roles"));
throw new ServletException("This route is restricted to administrators only");
}
}
request.setAttribute("claims", claims);
} catch (final SignatureException e) {
throw new ServletException("Invalid token");
}
chain.doFilter(req, res);
}
}
}
///////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////
// In addition, I changed the login code in the user controller
if (user.isAdmin()) {
jwtToken = Jwts.builder()
.setSubject(userName)
.claim("roles", "admin,user")
.setIssuedAt(new Date())
.signWith(SignatureAlgorithm.HS256, Constants.secretKey)
.compact();
} else {
jwtToken = Jwts.builder()
.setSubject(userName)
.claim("roles", "user")
.setIssuedAt(new Date())
.signWith(SignatureAlgorithm.HS256, Constants.secretKey)
.compact();
}
Technically, I could create a collection with the security mappings for role/uri, and loop through each one based on the URI being requested, without ever having to decorate a controller. 从技术上讲,我可以使用角色/ uri的安全性映射创建一个集合,并基于所请求的URI遍历每个集合,而无需装饰控制器。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.