ApplicationScope class 每次 Jersey 2.0 创建新实例
ApplicationScope class created new instance every time Jersey 2.0
我刚刚开始学习 oAuth2 授权来保护我的 API。而且过程相当复杂。我的 API 是使用 Jersey 开发的,Apache Oltu 作为 oAuth2 授权。现在,可以生成令牌,但是,每次我尝试生成新令牌时,服务器都会创建一个新的 @ApplicationScoped bean 实例。
这是我从网上得到的bean代码:
@ApplicationScoped
public class Database {
private Set<String> authCodes = new HashSet<>();
private Set<String> tokens = new HashSet<>();
public void addAuthCode(String authCode) {
authCodes.add(authCode);
}
public boolean isValidAuthCode(String authCode) {
return authCodes.contains(authCode);
}
public void addToken(String token) {
tokens.add(token);
}
public boolean isValidToken(String token) {
return tokens.contains(token);
}
}
这是授权码:
@Path("/authz")
public class AuthzEndpoint {
@Inject
Database database;
@GET
public Response authorize(@Context HttpServletRequest request)
throws URISyntaxException, OAuthSystemException {
try {
OAuthAuthzRequest oauthRequest = new OAuthAuthzRequest(request);
OAuthIssuerImpl oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());
//build response according to response_type
String responseType = oauthRequest.getParam(OAuth.OAUTH_RESPONSE_TYPE);
OAuthASResponse.OAuthAuthorizationResponseBuilder builder =
OAuthASResponse.authorizationResponse(request, HttpServletResponse.SC_FOUND);
if (responseType.equals(ResponseType.CODE.toString())) {
final String authorizationCode = oauthIssuerImpl.authorizationCode();
database.addAuthCode(authorizationCode);
builder.setCode(authorizationCode);
}
if (responseType.equals(ResponseType.TOKEN.toString())) {
final String accessToken = oauthIssuerImpl.accessToken();
database.addToken(accessToken);
builder.setAccessToken(accessToken);
builder.setExpiresIn(3600l);
}
String redirectURI = oauthRequest.getParam(OAuth.OAUTH_REDIRECT_URI);
final OAuthResponse response = builder.location(redirectURI).buildQueryMessage();
URI url = new URI(response.getLocationUri());
return Response.status(response.getResponseStatus()).location(url).build();
} catch (OAuthProblemException e) {
final Response.ResponseBuilder responseBuilder = Response.status(HttpServletResponse.SC_FOUND);
String redirectUri = e.getRedirectUri();
if (OAuthUtils.isEmpty(redirectUri)) {
throw new WebApplicationException(
responseBuilder.entity("OAuth callback url needs to be provided by client!!!").build());
}
final OAuthResponse response =
OAuthASResponse.errorResponse(HttpServletResponse.SC_FOUND)
.error(e).location(redirectUri).buildQueryMessage();
final URI location = new URI(response.getLocationUri());
return responseBuilder.location(location).build();
}
}
}
如你所见,这里有数据库的@Inject注解,并且在部分代码中调用了addToken()方法。当我尝试从我的主要 Web 服务验证令牌时,数据库 bean 是空的。这是代码
@Inject
Database database;
@POST
@Path("validateLogin")
@Consumes("application/x-www-form-urlencoded")
@Produces({"application/xml", "application/json", "text/plain", "text/html"})
public Response validateLogin(@HeaderParam("Authorization") String token, @FormParam("username") String username, @FormParam("password") String password) {
System.out.println(token.substring(7,token.length()));
System.out.println(database.isValidToken(token.substring(7, token.length())));
System.out.println(database);
if (!database.isValidToken(token.substring(7, token.length()))) {
return Response.status(Response.Status.UNAUTHORIZED).build();
}
else {
String result;
if (username == null || password == null) {
return Response.status(Response.Status.BAD_REQUEST).build();
}
else {
STCWebService stcWebService = new STCWebService();
result = stcWebService.validateLogin(username,password);
if (result.isEmpty()) {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
}
else {
return Response
.status(200)
.entity(result)
.type(MediaType.APPLICATION_JSON)
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT")
.build();
}
}
}
}
我一直在想 web.xml 文件或 ResourceConfig.java 甚至 ApplicationBinder 中可能存在一些错误。我已经偶然发现这个问题好几个小时了。感谢您的帮助。
编辑
这是 ApplicationBinder 代码
public class MyApplicationBinder extends AbstractBinder {
@Override
protected void configure() {
bind(Database.class).to(Database.class);
}
}
并链接到此 MyApplication
public class MyApplication extends ResourceConfig {
public MyApplication() {
register(new MyApplicationBinder());
packages("com.package");
}
}
我根据这个问题创建了 ApplicationBinder 和 MyApplication 代码 Dependency injection with Jersey 2.0
问题在于您如何绑定 Database
。
bind(Database.class).to(Database.class);
ApplicationScoped
将不起作用。您需要使用 AbstractBinder
配置范围。有几种方法。
实例化即可
bind(new Database()).to(Database.class);
这将自动使其成为单例。这样做的一个问题是,如果 Database
有它自己的依赖项,它们将不会被注入。为此,您可以使用下一个解决方案
使用in(Scope)
方法为服务添加范围。如果未提供,则默认范围为 PerLookup
,这意味着每次请求时都会创建一个新范围。还有其他范围,例如 Singleton
和 RequestScope
bind(Database.class).to(Database.class).in(Singleton.class);
确保它是 javax.inject.Singleton
而不是 EJB。
你应该在你链接到的那个问题中向下滚动到 my post:-)
我刚刚开始学习 oAuth2 授权来保护我的 API。而且过程相当复杂。我的 API 是使用 Jersey 开发的,Apache Oltu 作为 oAuth2 授权。现在,可以生成令牌,但是,每次我尝试生成新令牌时,服务器都会创建一个新的 @ApplicationScoped bean 实例。 这是我从网上得到的bean代码:
@ApplicationScoped
public class Database {
private Set<String> authCodes = new HashSet<>();
private Set<String> tokens = new HashSet<>();
public void addAuthCode(String authCode) {
authCodes.add(authCode);
}
public boolean isValidAuthCode(String authCode) {
return authCodes.contains(authCode);
}
public void addToken(String token) {
tokens.add(token);
}
public boolean isValidToken(String token) {
return tokens.contains(token);
}
}
这是授权码:
@Path("/authz")
public class AuthzEndpoint {
@Inject
Database database;
@GET
public Response authorize(@Context HttpServletRequest request)
throws URISyntaxException, OAuthSystemException {
try {
OAuthAuthzRequest oauthRequest = new OAuthAuthzRequest(request);
OAuthIssuerImpl oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());
//build response according to response_type
String responseType = oauthRequest.getParam(OAuth.OAUTH_RESPONSE_TYPE);
OAuthASResponse.OAuthAuthorizationResponseBuilder builder =
OAuthASResponse.authorizationResponse(request, HttpServletResponse.SC_FOUND);
if (responseType.equals(ResponseType.CODE.toString())) {
final String authorizationCode = oauthIssuerImpl.authorizationCode();
database.addAuthCode(authorizationCode);
builder.setCode(authorizationCode);
}
if (responseType.equals(ResponseType.TOKEN.toString())) {
final String accessToken = oauthIssuerImpl.accessToken();
database.addToken(accessToken);
builder.setAccessToken(accessToken);
builder.setExpiresIn(3600l);
}
String redirectURI = oauthRequest.getParam(OAuth.OAUTH_REDIRECT_URI);
final OAuthResponse response = builder.location(redirectURI).buildQueryMessage();
URI url = new URI(response.getLocationUri());
return Response.status(response.getResponseStatus()).location(url).build();
} catch (OAuthProblemException e) {
final Response.ResponseBuilder responseBuilder = Response.status(HttpServletResponse.SC_FOUND);
String redirectUri = e.getRedirectUri();
if (OAuthUtils.isEmpty(redirectUri)) {
throw new WebApplicationException(
responseBuilder.entity("OAuth callback url needs to be provided by client!!!").build());
}
final OAuthResponse response =
OAuthASResponse.errorResponse(HttpServletResponse.SC_FOUND)
.error(e).location(redirectUri).buildQueryMessage();
final URI location = new URI(response.getLocationUri());
return responseBuilder.location(location).build();
}
}
}
如你所见,这里有数据库的@Inject注解,并且在部分代码中调用了addToken()方法。当我尝试从我的主要 Web 服务验证令牌时,数据库 bean 是空的。这是代码
@Inject
Database database;
@POST
@Path("validateLogin")
@Consumes("application/x-www-form-urlencoded")
@Produces({"application/xml", "application/json", "text/plain", "text/html"})
public Response validateLogin(@HeaderParam("Authorization") String token, @FormParam("username") String username, @FormParam("password") String password) {
System.out.println(token.substring(7,token.length()));
System.out.println(database.isValidToken(token.substring(7, token.length())));
System.out.println(database);
if (!database.isValidToken(token.substring(7, token.length()))) {
return Response.status(Response.Status.UNAUTHORIZED).build();
}
else {
String result;
if (username == null || password == null) {
return Response.status(Response.Status.BAD_REQUEST).build();
}
else {
STCWebService stcWebService = new STCWebService();
result = stcWebService.validateLogin(username,password);
if (result.isEmpty()) {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
}
else {
return Response
.status(200)
.entity(result)
.type(MediaType.APPLICATION_JSON)
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT")
.build();
}
}
}
}
我一直在想 web.xml 文件或 ResourceConfig.java 甚至 ApplicationBinder 中可能存在一些错误。我已经偶然发现这个问题好几个小时了。感谢您的帮助。
编辑
这是 ApplicationBinder 代码
public class MyApplicationBinder extends AbstractBinder {
@Override
protected void configure() {
bind(Database.class).to(Database.class);
}
}
并链接到此 MyApplication
public class MyApplication extends ResourceConfig {
public MyApplication() {
register(new MyApplicationBinder());
packages("com.package");
}
}
我根据这个问题创建了 ApplicationBinder 和 MyApplication 代码 Dependency injection with Jersey 2.0
问题在于您如何绑定 Database
。
bind(Database.class).to(Database.class);
ApplicationScoped
将不起作用。您需要使用 AbstractBinder
配置范围。有几种方法。
实例化即可
bind(new Database()).to(Database.class);
这将自动使其成为单例。这样做的一个问题是,如果
Database
有它自己的依赖项,它们将不会被注入。为此,您可以使用下一个解决方案使用
in(Scope)
方法为服务添加范围。如果未提供,则默认范围为PerLookup
,这意味着每次请求时都会创建一个新范围。还有其他范围,例如Singleton
和RequestScope
bind(Database.class).to(Database.class).in(Singleton.class);
确保它是
javax.inject.Singleton
而不是 EJB。
你应该在你链接到的那个问题中向下滚动到 my post:-)