我看过 jersey 的名字绑定策略来屏蔽 api,对于身份验证来说非常酷,只对要认证的端点进行注释也很酷,并且在 jersey 中可以将注释绑定到过滤器。
有人知道如何在春季启动中实现这一目标吗?
我看过 jersey 的名字绑定策略来屏蔽 api,对于身份验证来说非常酷,只对要认证的端点进行注释也很酷,并且在 jersey 中可以将注释绑定到过滤器。
有人知道如何在春季启动中实现这一目标吗?
检查 maven 依赖 项 检查 maven 文件中应该有 spring-boot-starter-jersey 依赖项。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
创建 REST API
现在创建一些我们将在测试阶段访问的 JAX-RS 资源。我已经创建了UserResource
类。
用户资源.java
package com.howtodoinjava.jerseydemo;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "users")
@Path("/users")
public class UserResource
{
private static Map<Integer, User> DB = new HashMap<>();
@GET
@Produces("application/json")
public Users getAllUsers() {
Users users = new Users();
users.setUsers(new ArrayList<>(DB.values()));
return users;
}
@POST
@Consumes("application/json")
public Response createUser(User user) throws URISyntaxException
{
if(user.getFirstName() == null || user.getLastName() == null) {
return Response.status(400).entity("Please provide all mandatory inputs").build();
}
user.setId(DB.values().size()+1);
user.setUri("/user-management/"+user.getId());
DB.put(user.getId(), user);
return Response.status(201).contentLocation(new URI(user.getUri())).build();
}
@GET
@Path("/{id}")
@Produces("application/json")
public Response getUserById(@PathParam("id") int id) throws URISyntaxException
{
User user = DB.get(id);
if(user == null) {
return Response.status(404).build();
}
return Response
.status(200)
.entity(user)
.contentLocation(new URI("/user-management/"+id)).build();
}
@PUT
@Path("/{id}")
@Consumes("application/json")
@Produces("application/json")
public Response updateUser(@PathParam("id") int id, User user) throws URISyntaxException
{
User temp = DB.get(id);
if(user == null) {
return Response.status(404).build();
}
temp.setFirstName(user.getFirstName());
temp.setLastName(user.getLastName());
DB.put(temp.getId(), temp);
return Response.status(200).entity(temp).build();
}
@DELETE
@Path("/{id}")
public Response deleteUser(@PathParam("id") int id) throws URISyntaxException {
User user = DB.get(id);
if(user != null) {
DB.remove(user.getId());
return Response.status(200).build();
}
return Response.status(404).build();
}
static
{
User user1 = new User();
user1.setId(1);
user1.setFirstName("John");
user1.setLastName("Wick");
user1.setUri("/user-management/1");
User user2 = new User();
user2.setId(2);
user2.setFirstName("Harry");
user2.setLastName("Potter");
user2.setUri("/user-management/2");
DB.put(user1.getId(), user1);
DB.put(user2.getId(), user2);
}
}
用户.java
package com.howtodoinjava.jerseydemo;
import java.io.Serializable;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "user")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
@XmlAttribute(name = "id")
private int id;
@XmlAttribute(name="uri")
private String uri;
@XmlElement(name = "firstName")
private String firstName;
@XmlElement(name = "lastName")
private String lastName;
// Getters and Setters
}
配置泽西岛
现在我们有一个 JAX-RS 资源,我们想从包含 Jersey 依赖项的 Spring Boot 应用程序中访问它。让我们将此资源注册为 Jersey 资源。
包 com.howtodoinjava.jerseydemo;
导入 org.glassfish.jersey.server.ResourceConfig;导入 org.springframework.stereotype.Component;
@Component
public class JerseyConfig extends ResourceConfig
{
public JerseyConfig()
{
register(SecurityFilter.class);
register(UserResource.class);
}
}
看@Component
注释。它允许在 spring boot 自动扫描源文件夹中的 java 类时注册此类。
ResourceConfig
提供高级功能来简化
JAX-RS 组件的注册。
SecurityFilter
class 是我们使用 SpringBootServletInitializer 扩展 Spring Boot 应用程序。
package com.howtodoinjava.jerseydemo;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;
@SpringBootApplication
public class JerseydemoApplication extends SpringBootServletInitializer
{
public static void main(String[] args)
{
new JerseydemoApplication().configure(new SpringApplicationBuilder(JerseydemoApplication.class)).run(args);
}
}
使用 JAX-RS 注释保护 REST API
现在,当我们的 API 准备就绪时,我们将开始保护它们。让我们根据 API所需的访问级别和允许访问它们的用户角色,使用JAX-RS 注释对 API 进行注释。
package com.howtodoinjava.jerseydemo;
@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name = "users")
@Path("/users")
public class UserResource
{
private static Map<Integer, User> DB = new HashMap<>();
@GET
@PermitAll
@Produces("application/json")
public Users getAllUsers() {
Users users = new Users();
users.setUsers(new ArrayList<>(DB.values()));
return users;
}
@POST
@Consumes("application/json")
@RolesAllowed("ADMIN")
public Response createUser(User user) throws URISyntaxException
{
if(user.getFirstName() == null || user.getLastName() == null) {
return Response.status(400).entity("Please provide all mandatory inputs").build();
}
user.setId(DB.values().size()+1);
user.setUri("/user-management/"+user.getId());
DB.put(user.getId(), user);
return Response.status(201).contentLocation(new URI(user.getUri())).build();
}
@GET
@Path("/{id}")
@Produces("application/json")
@PermitAll
public Response getUserById(@PathParam("id") int id) throws URISyntaxException
{
User user = DB.get(id);
if(user == null) {
return Response.status(404).build();
}
return Response
.status(200)
.entity(user)
.contentLocation(new URI("/user-management/"+id)).build();
}
@PUT
@Path("/{id}")
@Consumes("application/json")
@Produces("application/json")
@RolesAllowed("ADMIN")
public Response updateUser(@PathParam("id") int id, User user) throws URISyntaxException
{
User temp = DB.get(id);
if(user == null) {
return Response.status(404).build();
}
temp.setFirstName(user.getFirstName());
temp.setLastName(user.getLastName());
DB.put(temp.getId(), temp);
return Response.status(200).entity(temp).build();
}
@DELETE
@Path("/{id}")
@RolesAllowed("ADMIN")
public Response deleteUser(@PathParam("id") int id) throws URISyntaxException {
User user = DB.get(id);
if(user != null) {
DB.remove(user.getId());
return Response.status(200).build();
}
return Response.status(404).build();
}
static
{
User user1 = new User();
user1.setId(1);
user1.setFirstName("John");
user1.setLastName("Wick");
user1.setUri("/user-management/1");
User user2 = new User();
user2.setId(2);
user2.setFirstName("Harry");
user2.setLastName("Potter");
user2.setUri("/user-management/2");
DB.put(user1.getId(), user1);
DB.put(user2.getId(), user2);
}
}
您可以在上面突出显示的行中看到与安全相关的 JAX-RS 注释。
使用 JAX-RS ContainerRequestFilter 编写安全过滤器
现在是时候编写我们的安全过滤器了,它将检查传入的请求,获取授权信息(本例中为基本身份验证),然后匹配用户名和密码,最后通过角色验证用户的访问级别。如果一切都匹配,将访问 API,否则用户将获得拒绝访问响应。
package com.howtodoinjava.jerseydemo;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Base64;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ResourceInfo;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.Provider;
/**
* This filter verify the access permissions for a user based on
* user name and password provided in request
* */
@Provider
public class SecurityFilter implements javax.ws.rs.container.ContainerRequestFilter
{
private static final String AUTHORIZATION_PROPERTY = "Authorization";
private static final String AUTHENTICATION_SCHEME = "Basic";
private static final Response ACCESS_DENIED = Response.status(Response.Status.UNAUTHORIZED).build();
private static final Response ACCESS_FORBIDDEN = Response.status(Response.Status.FORBIDDEN).build();
private static final Response SERVER_ERROR = Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
@Context
private ResourceInfo resourceInfo;
@Override
public void filter(ContainerRequestContext requestContext)
{
Method method = resourceInfo.getResourceMethod();
//Access allowed for all
if( ! method.isAnnotationPresent(PermitAll.class))
{
//Access denied for all
if(method.isAnnotationPresent(DenyAll.class))
{
requestContext.abortWith(ACCESS_FORBIDDEN);
return;
}
//Get request headers
final MultivaluedMap<String, String> headers = requestContext.getHeaders();
//Fetch authorization header
final List<String> authorization = headers.get(AUTHORIZATION_PROPERTY);
//If no authorization information present; block access
if(authorization == null || authorization.isEmpty())
{
requestContext.abortWith(ACCESS_DENIED);
return;
}
//Get encoded username and password
final String encodedUserPassword = authorization.get(0).replaceFirst(AUTHENTICATION_SCHEME + " ", "");
//Decode username and password
String usernameAndPassword = null;
try {
usernameAndPassword = new String(Base64.getDecoder().decode(encodedUserPassword));
} catch (Exception e) {
requestContext.abortWith(SERVER_ERROR);
return;
}
//Split username and password tokens
final StringTokenizer tokenizer = new StringTokenizer(usernameAndPassword, ":");
final String username = tokenizer.nextToken();
final String password = tokenizer.nextToken();
//Verifying Username and password
if(!(username.equalsIgnoreCase("admin") && password.equalsIgnoreCase("password"))){
requestContext.abortWith(ACCESS_DENIED);
return;
}
//Verify user access
if(method.isAnnotationPresent(RolesAllowed.class))
{
RolesAllowed rolesAnnotation = method.getAnnotation(RolesAllowed.class);
Set<String> rolesSet = new HashSet<String>(Arrays.asList(rolesAnnotation.value()));
//Is user valid?
if( ! isUserAllowed(username, password, rolesSet))
{
requestContext.abortWith(ACCESS_DENIED);
return;
}
}
}
}
private boolean isUserAllowed(final String username, final String password, final Set<String> rolesSet)
{
boolean isAllowed = false;
//Step 1. Fetch password from database and match with password in argument
//If both match then get the defined role for user from database and continue; else return isAllowed [false]
//Access the database and do this part yourself
//String userRole = userMgr.getUserRole(username);
String userRole = "ADMIN";
//Step 2. Verify user role
if(rolesSet.contains(userRole))
{
isAllowed = true;
}
return isAllowed;
}
}