This question already has answers here:
How to use parameters, request and session objects present in ActionContext?
(2 answers)
Closed 5 years ago.
What is best practice to implement HttpServletRequest in Struts2?
ServletActionContext - directly accessing the getRequest method from the ServletActionContext class
ServletRequestAware : the action class implements ServletRequestAware interface
I couldn't see (or find documentation about) the benefits of 2. vs 1.
So why should I prefer
public class MyAction extends ActionSupport implements ServletRequestAware {
private HttpServletRequest request;
#Override
public void setServletRequest(HttpServletRequest request) {
this.request = request;
}
}
When is worth enough
HttpServletRequest request = ServletActionContext.getRequest();
Related
Using the following dependencies (Gradle):
org.glassfish.jersey.containers:jersey-container-servlet:2.22.2
org.eclipse.jetty:jetty-servlet:9.3.2.v20150730
I have an embedded Jetty server, with a Jersey servlet container... something like this ...
package mypkg.rest.jersey;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.glassfish.jersey.server.ServerProperties;
import org.glassfish.jersey.servlet.ServletContainer;
import se.transmode.tnm.alarm.api.AlarmRetrieval;
import mypkg.rest.RestServer;
import mypkg.rest.jersey.serviceImpl.ModelAdapter;
public class JerseyBasedRestServer implements RestServer {
public static final int INITIALIZE_ON_USE = 0;
private Server server;
private final ServletContextHandler context;
private final ServletHolder servlet;
private final ModelAdapter modelAdapter;
public JerseyBasedRestServer(BusinessObjects businessObjects) {
this.modelAdapter = new ModelAdapter(businessObjects); //I want this instance to somehow be available for my ServletContainer to use.
context = new ServletContextHandler(ServletContextHandler.SESSIONS);
servlet = context.addServlet(ServletContainer.class, "/*");
servlet.setInitOrder(INITIALIZE_ON_USE);
servlet.setInitParameter(ServerProperties.PROVIDER_PACKAGES, "mypackage.jersey.generated.api.service");
servlet.setInitParameter(ServerProperties.MEDIA_TYPE_MAPPINGS, "json : application/json");
context.setContextPath("/");
}
private void startServlet() {
try {
servlet.start();
servlet.initialize();
} catch (Exception e) {
log.error("Failed to initialize servlet. {}", e.getMessage());
}
}
#Override
public void init(int port) {
server = new Server(port);
server.setHandler(context);
try {
server.start();
server.join();
startServlet();
} catch (Exception e) {
log.error("Failed to start jetty server for rest interface");
} finally {
server.destroy();
}
}
The Jersey Container will run server code and model generated using the Swagger code-gen tool
https://github.com/swagger-api/swagger-codegen#getting-started
which delivers the generated model, JacksonJsonProvider, and a RestApi class:
package mypackage.jersey.generated.api.service
Path("/")
public class RestApi {
private final RestApiService delegate = new RestApiServiceImpl(); //Integration point of the generated code
#GET
#Path("/list/")
#Consumes({ "application/json" })
#Produces({ "application/json" })
public Response retrieveAlarmList(#Context SecurityContext securityContext) throws NotFoundException {
return delegate.retrieveAlarmList(securityContext);
}
}
To integrate the generated code we are left to implement RestApiServiceImpl ourselves.
The ModelAdapter's job is to convert our business objects to the generated rest model.
So the question is how do I make the instance of the adapter of our business objects, in this case ModelAdapter, which lies outside the context of the Jersey servlet context, available to the RestApi class, or rather the RestApiServiceImpl?
I kind of understood from reading the past 24 hours that I need to use some sort of Context Dependency Injection either through Jetty, Jersey, or some other library (Weld seems to appear a lot), and have tried various combinations of #Inject, #Context, etc etc, but have come to the conclusion that I have no clue what I am actually doing... I'm not even sure I understand enough about the situation to phrase my question correctly.
More info can be made available on request.
Any help is appreciated.
EDIT: added a link here to https://github.com/englishbobster/JersetAndJetty
using #peeskillets suggestions, but still not working.
First thing you need to make DI work, is an AbstractBinder. This is where you will make your objects available to be injected.
class Binder extends AbstractBinder {
#Override
protected void configure() {
bind(modelAdapter).to(ModelAdapter.class);
}
}
Then you need to register the binder with Jersey. The easiest way is to register in Jersey's ResourceConfig. In your case, you are not using one. You are configuring everything in the "web.xml". For that, you should take a look at this post.
If you want to change your configuration to use a ResourceConfig, which personally I'd rather use, you can do this
package com.some.pkg;
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
packages("mypackage.jersey.generated.api.service");
property(ServerProperties.MEDIA_TYPE_MAPPINGS, "json : application/json");
register(new Binder());
}
}
Then to configure it with Jetty, you can do
servlet.setInitParameter(ServletProperties.JAXRS_APPLICATION_CLASS,
"com.some.pkg.JerseyConfig");
Now you can get rid of those other two init-params, as you are configuring it inside the ResourceConfig.
Another way, without any init-params, is to do
ResourceConfig config = new JerseyConfig();
ServletHolder jerseyServlet = new ServletHolder(ServletContainer(config));
context.addServlet(jerseyServlet, "/*");
See full example of last code snippet, here.
Now you can just inject the ModelAdapter pretty much anywhere within Jersey
In a field
#Inject
private ModelAdapter adapter;
Or in a contructor
#Inject
public RestApi(ModelAdapter adapter) {
this.adapter = adapter;
}
Or method parameter
#GET
public Response get(#Context ModelAdapter adapter) {}
I've got an app in asp.net core using the built-in DI framework. I'd like to add a per-request (i.e. transient) service to the provider, but I'd like to actually make use of the request in its construction.
services.AddTransient<IMyService>(provider => { ... });
That's the closest overload I can find, but the provider object doesn't have anything about the current request. Is there a way to achieve what I'm trying to do, without upgrading to a more robust DI framework?
As posted in the comments, you can inject the IHttpContextAccessor into your services and access it, if the HttpContext is the only thing you need.
public class MyService : IMyService
{
private readonly HttpContext context;
public MyService(IHttpContextAccessor httpContextAccessor)
{
if(IHttpContextAccessor==null)
throw new ArgumentNullException(nameof(httpContextAccessor));
context = httpContextAccessor.HttpContext;
}
}
However, if you need something that's only available in the controller or outside of HttpContext, you can create a factory and pass the parameters to the factory
public class MyServiceFactory : IMyServiceFactory
{
// injecting the HttpContext for request wide service resolution
public MyServiceFactory(IHttpContextAccessor httpContextAccessor) { ... }
public IMyService Create(IDependency1 dep1, IDependency2 dep 2, string someRuntimeConfig)
{
IServiceProvider provider = this.context.RequestServices;
var myService = new MyService(provider.GetService<ISomeRepository>(), dep1, dep2, someRuntimeConfig);
return myService;
}
}
and then inject the IMyServiceFactory to your classes where you'd need IMyService.
I'm using Spring Data Rest to expose a repository. I'm using #PreAuthorize and #PostFilter to restrict the access to the REST end points to exclusively admin users and filter the results.
#PreAuthorize("hasRole('ROLE_ADMIN')")
#PostFilter("hasPermission(filterObject, 'read')
public interface SomeRepository extends CrudRepository<SomeEntity, Long> {
}
At the same time I have another Controller that doesn't require any authentication but is using the repository.
#Controller
public class SomeController {
#Autowired
SomeRepository repository;
#RequestMapping(value = "/test")
public ResponseEntity test () {
// Do something
repository.findAll();
// Do something else
}
}
This doesn't work because the user that send the request to "/test" is not admin so it doesn't have access to the repository.
My question is, it is possible to add security exclusively to the REST interface of the repository and not when the repository is used internally in the application?
Thanks
Please evaluate these possibilities:
Security checks in REST event handlers
Adding custom repository methods for internal use
Using RunAsManager (or temporarily switching SecurityContext to perform a privileged operation)
Securing modifying requests using REST event handlers:
#Service
#RepositoryEventHandler
public class FooService {
/**
* Handles before-* events.
*/
#HandleBeforeCreate
#HandleBeforeSave
#HandleBeforeDelete
#PreAuthorize("hasRole('ADMIN')")
public void onBeforeModify(final Foo entity){
// noop
}
/**
* Handles before-* events.
*/
#HandleBeforeLinkSave
#HandleBeforeLinkDelete
#PreAuthorize("hasRole('ADMIN')")
public void onBeforeModifyLink(final Foo entity, final Object linked){
// noop
}
}
Securing standard CRUD methods while adding non-secure custom methods on repository for internal use:
public interface FooDao extends CrudRepository<Foo, Long> {
#Override
#PreAuthorize("hasRole('ADMIN')")
<S extends Foo> S save(final S entity);
/**
* Saves entity without security checks.
*/
#Transactional
#Modifying
default <S extends Foo> S saveInternal(final S entity) {
return save(entity);
}
}
One solution would be to remove the #PreAuthorize annotation from your repository interface, and in a configuration class, extend WebSecurityConfigAdaptor and override the configure(HttpSecurity security) method. From here you can use AntMatchers to impose access restrictions to the REST endpoints as required. For example:
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/someEntities/**").hasRole('ADMIN')
.anyRequest().permitAll();
}
See http://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#jc-httpsecurity for more details.
I ran into the same problem and came up with a workaround that doesn't feel completely right but does its job for the time being.
I basically created a security utils bean which can be used to check if a method was called internally or externally using the Spring Data REST API (remark: my repositories are prefixed /api/, if you have another prefix you need to change the regex accordingly).
#Component("securityUtils")
public class SecurityUtils {
public boolean isRestRequest(){
HttpServletRequest r = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
return Pattern.matches("^/api/", UrlUtils.buildRequestUrl(r));
}
}
To make this work, you need to add the following line to your listeners in the web.xml:
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
And use the method in your expression based access control like so (where the last line in the expression allows you to use the save method from any controller methods that are mapped against URLs which do not start with /api/:
#Override
#PreAuthorize("hasRole('ROLE_ADMINISTRATOR') " +
"or hasPermission(#user, 'WRITE') " +
"or !#securityUtils.isRestRequest()")
<S extends User> S save(#P("user") S user);
Caveats:
You cannot use this when you want to expose custom functionality over the /api route as this is merely a simple regex check against the route
The check has to be explicitly added to each repository or repository method for which you want to omit the authorization check internally (might be an advantage as well)
In my opinion the right solution would be to have two Repositories, one that is called EntityRepository and one SecuredEntityRepository.
Example:
#RestResource(exported = false)
public abstract interface CustomerRepository extends JpaRepository<Customer, Long> {
}
and the secured version:
#RestResource(exported = true)
public abstract interface SecuredCustomerRepository extends CustomerRepository {
#Override
#PreAuthorize("#id == principal.customer.id or hasAuthority('ADMIN_CUSTOMER_ONE')")
public Customer findOne(#Param("id") Long id);
#Override
#Query("SELECT o FROM #{#entityName} o WHERE o.id = ?#{principal.customer.id} or 1 = ?#{ hasAuthority('ADMIN_CUSTOMER_LIST') ? 1 : 0 }")
public Page<Customer> findAll(Pageable pageable);
#Override
#SuppressWarnings("unchecked")
#PreAuthorize("#customer.id == principal.customer.id or hasAuthority('ADMIN_CUSTOMER_SAVE')")
public Customer save(#P("customer") Customer customer);
#Override
#PreAuthorize("hasAuthority('ADMIN_CUSTOMER_DELETE')")
public void delete(#Param("id") Long id);
#Override
#PreAuthorize("hasAuthority('ADMIN_CUSTOMER_DELETE')")
public void delete(Customer customer);
}
This is currently not possible due to an issue with the auto-wiring mechanism in SD REST: https://jira.spring.io/browse/DATAREST-923
Sure. Just change the location of the #PreAuthorize annotation. This annotation can be placed in classes or single methods.
For example
#Controller
public class SomeController {
#Autowired
SomeRepository repository;
#RequestMapping(value = "/test")
#PreAuthorize(....)
public ResponseEntity test () {
// Do something
repository.findAll();
// Do something else
}
}
is perfectly legit (note the annotation on the test() method.
I decorated the repository class with this:
#PreAuthorize("hasRole('admin')")
It locked down everything.
Then whatever I wanted to enable for internal use but not rest, I decorated like this:
#Transactional
#Modifying
#PreAuthorize("hasRole('user')")
#RestResource(exported = false)
default <S extends SomeEntity> S saveInternal(final S entity) {
return save(entity);
}
And whatever I wanted to expose via the Rest interface (handpicked few) I exposed with something like this:
#PreAuthorize("(hasRole('user')) and
(#entity.user.username == principal.name)")
#Override
<S extends SomeEntity> S save(#Param("entity") S entity);
Note that this also validates that you are saving a record you are authorized to save.
I solved this problem by adding my own check
I created my AbstractHttpConfigurer class with global security. I have declared methods that can be public.
public class CommonSpringKeycloakTutorialsSecurityAdapter extends AbstractHttpConfigurer<CommonSpringKeycloakTutorialsSecurityAdapter, HttpSecurity> {
public static String[] PERMIT_ALL_URL = {"/api/user/createUser"};
#Override
public void init(HttpSecurity http) throws Exception {
// any method that adds another configurer
// must be done in the init method
http
// disable csrf because of API mode
.csrf().disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
// manage routes securisation here
.authorizeRequests().antMatchers(HttpMethod.OPTIONS).permitAll()
// manage routes securisation here
.and()
.authorizeRequests()
.antMatchers(HttpMethod.OPTIONS).permitAll()
.antMatchers("/swagger-ui.html*", "/swagger-ui/**", "/v3/api-docs/**").permitAll()
.antMatchers(PERMIT_ALL_URL).permitAll()
.anyRequest().authenticated();
}
Then I created my own check based on global permissions.
#Component("securityUtils")
public class SecurityUtils {
public boolean isPermitRestRequest(){
HttpServletRequest r = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
String currentUrl = UrlUtils.buildRequestUrl(r);
for(String url: CommonSpringKeycloakTutorialsSecurityAdapter.PERMIT_ALL_URL) {
if(currentUrl.equals(url)) {
return true;
}
}
return false;
}
}
For native validation to work, include a listener
#WebListener
public class MyRequestContextListener extends RequestContextListener {
}
In my team we evaluated several of the answers in this post and they didn't fit to our scenario.
A variation of Johannes Hiemer answer worked for us. We configured Spring Data REST to only expose annotated repositories:
data.rest:
detection-strategy: annotated
Then we defined 2 repositories without hierarchical relationship.
One of the repos will be exposed by adding the #RepositoryRestResource annotation to it. For this one, we deny access to every method by default so auth will have to be specified on a method level to reduce the chances of exposing methods by mistake. For example, initially we extended CrudRepository and didn't want to expose the deletion operation:
#RepositoryRestResource
#PreAuthorize("denyAll()")
interface SomeRestResourceRepository : Repository<SomeEntity, Long> {
}
The repository to be used for internal calls is defined as a regular Spring Data Repository:
interface SomeRepository : Repository<SomeEntity, Long> {
}
We are using spring-boot-starter-data-rest 2.6.3.
Our company has the need to log certain things each time one of our action methods of our ASP.NET WebApi controllers gets called. Since we use Ninject for the DI right now, we'd like to use it also for this purpose. This is what I have tried so far.
I have Ninject, Ninject.Extensions.Interception and Ninject.Extensions.Interception.DynamicProxy installed through NuGet and I have the following module
public class InterceptAllModule : InterceptionModule
{
public override void Load()
{
Kernel.Intercept(p => p.Request.Service.Name.EndsWith("Controller")).With(new TimingInterceptor());
}
}
Where TimingInterceptor is
public class TimingInterceptor : SimpleInterceptor
{
readonly Stopwatch _stopwatch = new Stopwatch();
protected override void BeforeInvoke(IInvocation invocation)
{
_stopwatch.Start();
}
protected override void AfterInvoke(IInvocation invocation)
{
_stopwatch.Stop();
string message = string.Format("[Execution of {0} took {1}.]",invocation.Request.Method,_stopwatch.Elapsed);
Log.Info(message + "\n");
_stopwatch.Reset();
}
}
Now, when I try to hook the module up with ninject kernel, and run my site
var kernel = new StandardKernel(new InterceptAllModule());
However, whenever there is a call coming in to one of the action method, it throws an error saying
Cannot instantiate proxy of class: MyApiController.
Could someone with experience point out what I'm doing wrong please? Thanks.
Update
So using your Code and Remo's excellent point about needing the action methods to be virtual and putting in an empty default constructor (just to placate dynamic proxy, keep your other constructor still) I have got both the action filter and the interception approach working.
I would say that as it stands your code will intercept potentially unwanted methods on the ApiController so you will probably also need to put some code in place to filter these out e.g. ExecuteAsync and Dispose.
My only other point is performance. Huge disclaimer these are just very basic tests (using the action filter approach each time to log the stats), I invite you to do your own(!)... but using the DynamicProxy interceptor I was getting a time of around 4 milliseconds per get request
[Execution of Get took 00:00:00.0046615.]
[Execution of Get took 00:00:00.0041988.]
[Execution of Get took 00:00:00.0039383.]
Commenting out the Interception code and using an Action filter I was getting sub millisecond performance:
[Execution of Get took 00:00:00.0001146.]
[Execution of Get took 00:00:00.0001116.]
[Execution of Get took 00:00:00.0001364.]
It is up to you whether this is actually an issue or concern but I thought I would point this out.
Previous Response
Have you rulled out using ActionFilters? This is the natural extension point for AOP on an MVC action.
If you were interested in methods other than the actual action on the controller then I would understand but I thought I would post a suggestion anyway.
Inspired by Are ActionFilterAttributes reused across threads? How does that work? and Measure Time Invoking ASP.NET MVC Controller Actions.
Updated to show the exclusion of the timer when method tagged. Inspiration from core WebApi framework specifically AllowAnonymousAttribute and AuthorizeAttribute
Register this globally so that all actions are monitored by this:
GlobalConfiguration.Configuration.Filters.Add(new TimingActionFilter());
Then:
public class TimingActionFilter : ActionFilterAttribute
{
private const string Key = "__action_duration__";
public override void OnActionExecuting(HttpActionContext actionContext)
{
if (SkipLogging(actionContext))
{
return;
}
var stopWatch = new Stopwatch();
actionContext.Request.Properties[Key] = stopWatch;
stopWatch.Start();
}
public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
if (!actionExecutedContext.Request.Properties.ContainsKey(Key))
{
return;
}
var stopWatch = actionExecutedContext.Request.Properties[Key] as Stopwatch;
if(stopWatch != null)
{
stopWatch.Stop();
var actionName = actionExecutedContext.ActionContext.ActionDescriptor.ActionName;
Debug.Print(string.Format("[Execution of {0} took {1}.]", actionName, stopWatch.Elapsed));
}
}
private static bool SkipLogging(HttpActionContext actionContext)
{
return actionContext.ActionDescriptor.GetCustomAttributes<NoLogAttribute>().Any() ||
actionContext.ControllerContext.ControllerDescriptor.GetCustomAttributes<NoLogAttribute>().Any();
}
}
And
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true)]
public class NoLogAttribute : Attribute
{
}
Now you can exclude the global filter using:
public class ExampleController : ApiController
{
// GET api/example
[NoLog]
public Example Get()
{
//
}
}
For anyone still lurking, the reason I wanted to use Ninject was so I could inject a logger (or anything else) into the interceptor, but I wanted to intercept all actions.
Mark's answer is perfect, but instead of registering globally using
GlobalConfiguration.Configuration.Filters.Add(new TimingActionFilter());
bind your filter with Ninject using
Kernal.BindHttpFilter<TimingActionFilter>(FilterScope.Action).
You'll need to create an appropriate contructor in the TimingActionFilter class.
I'm relatively new to Struts2.
I've started using ModelDriven to reduce overhead in development.
I wanted to write an interface to modify a property before it gets to the action but I don't see how you can access the properties of a class that implements ModelDriven for the Model.
I can see how things like validate() can work as they are in the actual action class.
I changed the design to encapsulate the logic behind the service anyways but still would like to know if this is possible.
We're doing everything by ajax/json so I find modeldriven helps quite a lot - not sure if there is a better alternative though!
Edit - code example:
Trying to replace a message with a message in a template to use in an email body.
public class EmailActionImpl implements EmailAction {
private Email email=new Email();
private EmailService emailService;
public Email getModel(){
return email;
}
[... getters and setters ...]
public String execute(){
logger.info("Email action is sendind an email...");
try{
emailService.sendNewMail(email);
}catch(Exception e){
logger.error("Email not sent: " + e.getMessage());
return "failure";
}
return "success";
}
}
Email model something like this
#Entity
#Table(name="email")
public class Email {
private Long id;
private String from;
private String to;
private String message;
private String templateType;
[...]
}
I would like an interceptor preprocessor to replace email.message.
Should look something like this but action.getMessage/setMessage aren't available.
public class SimpleInterceptor extends AbstractInterceptor {
public String intercept(ActionInvocation invocation) throws Exception {
EmailAction action = (EmailAction)invocation.getAction();
action.setMessage(MessageTemplateFactoryImpl(action.getMessage(), action.getTemplateType());
return invocation.invoke();
}
}
If you still want to implement an interceptor to work on a particular set of models then you will check if the Action implements ModelDriven. Via reflection (or Apache bean utils) you can derive the particular model in question, to determine if your interceptor applies and then act on it accordingly.