Authenticate Operation Before Each Action In Spring Boot Effectively - spring-boot

I have a code block like below. For each method, I call action authentication. Is it possible to do with annotation or other thing effectively?
#GetMapping
public ResponseEntity getAction(#PrincipalUser user, Long actionId)
{
repository.checkUserForAction(user.getId(), actionId);
implement actions...
return service call;
}
#PostMapping
public ResponseEntity addAction(#PrincipalUser user)
{
repository.checkUserForAction(user.getId());
implement actions...
return service call;
}
#DeleteMapping
public ResponseEntity addAction(#PrincipalUser user, Long actionId)
{
repository.checkUserForAction(user.getId(), actionId);
implement actions...
return service call;
}
Actually, in here my other problem is that I call repository method each time and I know this is not an effective way.

You could use Spring Security and #PreAuthorize annotation.
Example:
#PreAuthorize("#authorizationService.check(#actionId)")
#DeleteMapping
public ResponseEntity performAction(Long actionId) {
implement actions...
}
And encapsulate authorization logic inside authorizationService, moreover authorizationService must be a bean and #EnableGlobalMethodSecurity(prePostEnabled = true) must be set.

Related

How to pass attribute to Spring Controller in a stateless application?

I am currently implementing a SAML SSO solution in my application where in my SAMLUserDetailsService, I am loading my user
#Service
public class SAMLUserDetailsServiceImpl implements SAMLUserDetailsService {
#Override
public Object loadUserBySAML(SAMLCredential credential) throws UsernameNotFoundException {
return new User(credential.getNameID().getValue());
}
}
I am then using a SavedRequestAwareAuthenticationSuccessHandler to redirect user to a landing controller upon successful authentication.
#Bean
public AuthenticationSuccessHandler successRedirectHandler() {
SavedRequestAwareAuthenticationSuccessHandler successRedirectHandler =
new SavedRequestAwareAuthenticationSuccessHandler();
successRedirectHandler.setDefaultTargetUrl("/landing");
return successRedirectHandler;
}
Controller:
#RequestMapping("/landing")
public ResponseEntity landing(User user) {
return ResponseEntity.ok(user.getLoginName());
}
Is there a way to pass the User object to my controller. I noticed that this is usually done using a HandlerMethodArgumentResolver but since my application is stateless and does not use sessions, is there a way to achieve this using another way please?
You don't need injection for this. Use following instead:
SecurityContextHolder.getContext().getAuthentication().getName()
or
SecurityContextHolder.getContext().getAuthentication().getPrincipal()
In the latter case check the type of what getPrincipal() returned. It can be String, it can be UserDetails. If latter, cast it to UserDetails and call getUsername().

Pass parameter to controller

I have some app with JWT authentication. And currently, I have such controller:
#RestController
#RequestMapping("users")
public class UserController {
#PostMapping(value = "{userId}/rate/inc")
public Double incRate(#PathVariable Long userId) {
return service.incUserRate(userId);
}
}
But, I want to get user by the token in the filter and pass it as a method's param. For example:
#PostMapping(value = "/rate/inc")
public Double incRate(User user) {
returnservice.incUserRate(user);
}
Is this possible?
Implement argument resolver and inject into your controller everything you need.
By default Spring allowes you to inject Principal object that by default contains users email (it is default realization in Spring Security). But you can implement injection of your business login users account by implementing Interface HandlerMethodArgumentResolver<User>.
I advice you to create an annotation like #AuthorizedUser in make mark your User param with this annotation. And according to this annotation presence in controller method, inject your user via HandlerMethodArgumentResolver.
#Component
public class UserArgumentHandlerResovler implements HandlerMethodArgumentResolver {
#Autowired
private UserRepository userRepository;
public boolean supportsParameter(MethodParameter parameter) {
return parameter.isAnnotationPresent(AuthorizedUser.class);
}
public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
String email = (String) auth.getPrincipal(); // <- it is a pseudocode, check your Authentication implementation to get email for example.
return userRepository.findByEmail(email);
}
}
If you use Spring Security, you can resolve the current user and then have it provided to your controller method. However – if I'm not mistaken – you must declare it as Principal:
#PostMapping(value = "/rate/inc")
public Double incRate(Principal principal) {
returnservice.incUserRate((User)principal);
}
A more extensive example can be found at Baeldung.

How to access value cached using spring #cacheable in other controllers?

I have a frequently accessed but bulky value - valueA which is obtained by a method - methodA of rest controller - ControllerA. So I have cached this value using #Cacheable annotation as follows.
#CacheConfig(cacheNames={"abc_cache"})
public class ControllerA
{
#Cacheable
#RequestMapping(value = "/value/" , method=RequestMethod.GET)
public ResponseEntity<Value> fetchValue()
{
// some logic
return new ResponseEntity<Value>(value, HttpStatus.OK);
}
}
I want to access this value in another method - methodB of another controller - controllerB.
How can I access this value?
You can have some other class/bean to supply that value. Then you can inject that bean in all the controllers that you want.
Something like this:
#Component
public class MyValueService {
#Cacheable
public Value getValue() {
return ...;
}
}
and then in the controllers
public class ControllerA
{
#Autowired
private MyValueService valServ;
#RequestMapping(value = "/value/" , method=RequestMethod.GET)
public ResponseEntity<Value> fetchValue()
{
return new ResponseEntity<Value>(valServ.getValue(), HttpStatus.OK);
}
}
Are you aware of the Controller -> Service -> Repository pattern?
Basically:
Controllers are the web layer. They handle http requests. They use Services.
Services are responsible for the business logic of the application. They use Repositories.
Repositories are responsible for the data access - databases access, read/write from the file system, etc.
You should structure your application that way.
In general I would use caching on the Repository layer. Usually the bottle neck are the I/O operations (read/write to the file system, DB call, calling external service over the network) and that are the things that you want to cache if the possible.
I think you should just encapsulate your cacheable logic into a method in another class and then just call it from both controllers.
So
public class ControllerA
{
#Resource
private Service service;
#RequestMapping(value = "/value/" , method=RequestMethod.GET)
public ResponseEntity<Value> fetchValue()
{
// some logic
Object v = service.cachedMethod();
return new ResponseEntity<Object>(v, HttpStatus.OK);
}
}
and
#Component
public class Service {
#Cacheable
public Object cachedMethod() {... }
}

Injecting Custom Principal to Controllers by Spring Security

servletApi() support of Spring Security is great.
I want to inject custom Principal as this:
public interface UserPrincipal extends Principal {
public Integer getId();
}
#RequestMapping(value = "/")
public ResponseEntity<List<Conversation>> listAfter(UserPrincipal user){
// implementation
}
or
#RequestMapping(value = "/")
public ResponseEntity<List<Conversation>> listAfter(UserPrincipalImpl user){
// implementation
}
Spring has support for injecting Principal instances with the help of ServletRequestMethodArgumentResolver.
It is injecting principal as this:
else if (Principal.class.isAssignableFrom(paramType)) {
return request.getUserPrincipal();
}
Here is the place where the problem begins. request is here an instance of SecurityContextHolderAwareRequestWrapper. It has an implementation of:
#Override
public Principal getUserPrincipal() {
Authentication auth = getAuthentication();
if ((auth == null) || (auth.getPrincipal() == null)) {
return null;
}
return auth;
}
Because an Authentication is also an Principal. (The only part of spring security I did not like so far. I will ask this a separate question as well.)
This is causing a problem. Because Authentication is a Principal not a UserPrincipal.
How can I resolve this problem? Do I need to implement an authentication which is a UserPrincipal as well? Or should I change HandlerMethodArgumentResolver order a create a custom resolver? (This is not easy for Spring MVC because internal handlers has higher priority.)
As a extra information:
I am using Spring Security M2 and my configuration for AuthenticationManagerBuilder is simply:
#Override
protected void registerAuthentication(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(detailsService);
}
Any help?
Fundamentally this seems like trouble integrating with Spring MVC and not a Spring Security issue. Spring Security has no way of knowing that Authentication#getPrinicpal() implements Principal since the API returns an Object.
I see a few options for you. Each has some pros and cons, but I think the best is using #ModelAttribute and #ControllerAdvice
#ModelAttribute and #ControllerAdvice
The easiest option is annotate a method with #ModelAttribute on custom #ControllerAdvice. You can find details in the Spring Reference.
#ControllerAdvice
public class SecurityControllerAdvice {
#ModelAttribute
public UserPrincipal customPrincipal(Authentication a) {
return (UserPrincipal) a == null ? null : a.getPrincipal();
}
}
Now in your controller you can do something like this:
#RequestMapping(value = "/")
public ResponseEntity<List<Conversation>> listAfter(#ModelAttribute UserPrincipal user){
// implementation
}
Note that the #ModelAttribute is necessary only to ensure the #ModelAttribute is used over the HttpServletRequest#getPrincipal(). If it did not implement Principal, #ModelAttribute is not required.
#Value and ExpressionValueMethodArgumentResolver
You can also do something like this:
#RequestMapping(value = "/")
public ResponseEntity<List<Conversation>> listAfter(
#Value("#{request.userPrincipal.principal}") UserPrincipal user){
// implementation
}
This works because the HttpServletRequest is available as an attribute to the ExpressionValueMethodArgumentResolver (added by default by Spring MVC) which allows accessing things via SpEL. I find this less attractive than #ModelAttribute due to the constant that must be in the #Value annotation. It will be nicer when SPR-10760 is resolved which would allow for your own custom annotation to be used like:
#Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Value("#{request.userPrincipal.principal}")
public #interface CurrentUser { }
#Autowire RequestMappingHandlerAdapter
This is a bit sloppy because the RequestMappingHandlerAdapter has already been initialized, but you can change the ordering of the HandlerMethodArgumentResolvers as shown here:
#EnableWebMvc
#Configuration
public class WebMvcConfiguration
extends WebMvcConfigurerAdapter {
...
#Autowired
public void setArgumentResolvers(RequestMappingHandlerAdapter adapter) {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
resolvers.add(new CustomPrincipalArgumentResolver());
resolvers.addAll(adapter.getArgumentResolvers().getResolvers());
adapter.setArgumentResolvers(resolvers);
}
}
Subclass WebMvcConfigurationSupport
You can also extend WebMvcConfigurationSupport instead of using #EnableWebMvc to ensure your HandlerMethodArgumentResolver is used first. For example:
#Configuration
public class WebConfiguration extends WebMvcConfigurationSupport {
...
#Bean
#Override
public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
RequestMappingHandlerAdapter adapter = super.requestMappingHandlerAdapter()();
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
resolvers.add(new CustomPrincipalArgumentResolver());
resolvers.addAll(adapter.getArgumentResolvers().getResolvers());
adapter.setArgumentResolvers(resolvers);
return adapter;
}
}
I know this is an old question, but as it does come up on top on Google when searching for injecting a Principal, I'll post a 2020 update:
Since Spring Security 4.0 you can just simply inject an #AuthenticationPrincipal into your controller methods:
#RequestMapping(value = "/")
public ResponseEntity<List<Conversation>> listAfter(#AuthenticationPrincipal UserPrincipal user){
// implementation
}
This will work out of the box, no additional config required.

Check the state validity of a Spring proxied bean without try-catch

I have a bean being created by a service with the following class:
#Configuration
public class AccessManager {
#Bean(name="access", destroyMethod="destroy")
#Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
#Autowired
public Access create(HttpServletRequest request) {
System.out.println(request.getRemoteAddr());
return new Access();
}
}
Everything works as expected, except that when the application is starting, this method is being called, probably because I have some other singleton beans that use the Access bean. At the start up there is no request bound to the Thread, and it's expected to get a java.lang.IllegalStateException when trying to access any property of the request parameter.
No problem. The question is, is it possible to check if the underlying HttpServletRequest of the proxy request is null before calling a property that raises the exception?
You probably want to take a look at RequestContextHolder#getRequestAttributes(). That will return null if you're not currently in a context where request scope could be used.
#Configuration
public class AccessManager {
#Bean(name="access", destroyMethod="destroy")
#Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS)
#Autowired
public Access create(HttpServletRequest request) {
if (RequestContextHolder.getRequestAttributes() != null) {
System.out.println(request.getRemoteAddr());
}
return new Access();
}
}
I think the issue here is with separation of concerns. Usually your service layer should not have any dependency on the servlet classes. This is very much a controller/UI concern.
Your service class should be provided with the properties which it needs to do its job. In this case a String. This service method should be called from a controller method which is injected with the servlet request.
Something like the following:
#Controller
public class MyController {
#Autowired
private AccessManager accessManager;
#RequestMapping
public void handleRequest(HttpServletRequest request) {
accessManager.create(request.getRemoteAddr());
}
}
and your service would then look like this:
#Service
public class AccessManager {
public Access create(String remoteAddress) {
return new Access();
}
}
To sum up, anything annotated as #Service shouldn't have access to the Request.

Resources