I'm using OpenApi for Spring Boot application and I have authorization logic with JWT. The authorization request at /api/v1/login is intercepted and JSON is returned with the user token:
{
"Bearer": "token for user"
}
Security implementation responsible for capturing logins:
#Component
#RequiredArgsConstructor
class RestAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication authentication) throws IOException {
// handler returns body JSON with JWT
}
}
#Configuration
class SecurityConfig extends WebSecurityConfigurerAdapter {
// ...
JsonObjectAuthenticationFilter authenticationFilter() throws Exception {
var authFilter = new JsonObjectAuthenticationFilter();
authFilter.setAuthenticationSuccessHandler(restAuthenticationSuccessHandler );
authFilter.setAuthenticationFailureHandler(RestAuthenticationFailureHandler );
authFilter.setAuthenticationManager(super.authenticationManager());
authFilter.setFilterProcessesUrl("/api/v1/login"); // <- custom login URL
return authFilter;
}
}
It works fine, I don't have to put a separate /api/v1/login endpoint in the controller, so it is not taken into account when creating OpenAPI documentation. However, I want to have this endpoint documented and accessible from there as follows:
My first idea was to just create an interface to add appropriate annotations (assume BearerToken and AuthCredentials are my tranfer objects in correct format):
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("/api/v1/login")
#Tag(name = "login")
interface LoginController {
#PostMapping
BearerToken login(#RequestBody AuthCredentials authCredentials);
}
However Spring does not register the interface as a beana, an implementation has yet to be provided, so OpenAPI does not add an entry to the documentation. So I turned the interface into a normal class:
#RestController
#RequestMapping("/api/v1/login")
#Tag(name = "login")
class LoginController {
#PostMapping
public BearerToken login(#RequestBody AuthCredentials authCredentials){
return new BearerToken();
}
}
Documentation is generated correctly, but I have a problem with this method. The implementation of the login() method suggests a completely different behavior than what actually takes place underneath in onAuthenticationSuccess().
Therefore, I am looking for a different way to achieve the desired effect.
As per OpenAPI implementation, there is a class OpenAPIService which has a build() method that does the following:
this.mappingsMap.putAll(this.context.getBeansWithAnnotation(RestController.class));
this.mappingsMap.putAll(this.context.getBeansWithAnnotation(RequestMapping.class));
this.mappingsMap.putAll(this.context.getBeansWithAnnotation(Controller.class));
The reason as to why what you want to achieve is not possible is that OpenAPI library relies on Spring annotations that I provided above and has no knowledge of custom ant matcher paths like the one you added:
JsonObjectAuthenticationFilter authenticationFilter() throws Exception {
var authFilter = new JsonObjectAuthenticationFilter();
authFilter.setAuthenticationSuccessHandler(restAuthenticationSuccessHandler );
authFilter.setAuthenticationFailureHandler(RestAuthenticationFailureHandler );
authFilter.setAuthenticationManager(super.authenticationManager());
authFilter.setFilterProcessesUrl("/api/v1/login"); // <- custom login URL
return authFilter;
}
Usually in production-ready applications developers write their own /authenticate API that checks username + password pair against a datasource (mySQL/postgreSQL/other).
This /authenticate API would be whitelisted so that security is not required to attempt login (some people store number of attempts per IP in Redis in case they want to block people from bruteforcing the password).
All other APIs would have to go through public class JwtAuthenticationFilter extends AuthenticationFilter { which simply validates the token and allows request to continue (if token is valid).
If you need more hints let me know.
I am using OAuth2RestTemplate in order to pass an oauth token with REST requests. However, I now need to hardcode my urls, such as
restTemplate.postForLocation("http://localhost:5555/other-service2/message", "Message")
whereas when I was using a self created Ribbon Annotated(using #LoadBalanced) RestTemplate bean, I can do something like
restTemplate.postForLocation("http://service1/other-service2/message", "Message")
This is because when you use LoadBalanced, it will automatically make it a Ribbon Rest Template which lets you use the Service Discovery features or Eureka, but when you annotate a OAuth2RestTemplate bean with #Loadbalanced, it would throw some sort of error at runtime when trying to use the OAuth2RestTemplate, which says
o.s.b.a.s.o.r.UserInfoTokenServices : Could not fetch user details: class java.lang.IllegalStateException, No instances available for localhost
My OAuth2RestTemplate creation looks like
#LoadBalanced
#Bean
public OAuth2RestTemplate restTemplate(final UserInfoRestTemplateFactory factory) {
final OAuth2RestTemplate userInfoRestTemplate = factory.getUserInfoRestTemplate();
return userInfoRestTemplate;
}
How can I use the Service Discovery features as well as load balancing features of Eureka's Ribbon, on an OAuth2RestTemplate?
I think this is something you might try.
In my project we also use OAuth2, Eureka, Ribbon for microservices to communicate each other. In order to use Ribbon with OAuth2, the approach we took was bit different.
First we leave the restTemplate untouched.
#LoadBalanced
#Bean
public RestTemplate restTemplate() {
However, we created FeignClientIntercepter implementing RequestIntercepter which sets authorization tokens for OAuth when making a request via restTemplate.
#Component
public class UserFeignClientInterceptor implements RequestInterceptor {
private static final String AUTHORIZATION_HEADER = "Authorization";
private static final String BEARER_TOKEN_TYPE = "Jwt";
#Override
public void apply(RequestTemplate template) {
SecurityContext securityContext = SecurityContextHolder.getContext();
Authentication authentication = securityContext.getAuthentication();
if (authentication != null && authentication
.getDetails() instanceof OAuth2AuthenticationDetails) {
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication
.getDetails();
template.header(AUTHORIZATION_HEADER,
String.format("%s %s", BEARER_TOKEN_TYPE, details.getTokenValue()));
}
}
}
And If you try to create spring msa project, I would prefer using Feign-client rather than restTemplate.
#FeignClient("your-project-name")
public interface YourProjectClient {
#GetMapping("your-endpoint")
JsonObject getSomething();
How to perform validation with Spring Cloud Stream framework in message listeners using standard Spring annotation based validation?
Tried different cases, with #Valid #Payloadfor incoming object, tried method validation post processor with #Validated on entity, but it didn't help.
#StreamListener(MediaItemStream.ITEM_LIKED_CHANNEL)
public void handleLikeMessage(#Valid #Payload LikeInputDto like) {...
and
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
The best approach for now is just using of custom service for validation, but it looks not as wanted..
#Log4j2
#Service
#AllArgsConstructor
public class LikeStreamHandler {
private MediaEventMessagingService mediaEventMessagingService;
private ValidationService validationService;
#StreamListener(MediaItemStream.ITEM_LIKED_CHANNEL)
public void handleLikeMessage(LikeInputDto like) {
validationService.validate(like);
log.debug("Handling LIKE message: {}", like);
mediaEventMessagingService.processLikeEvent(like);
}
}
This is a new Feature of Spring Cloud Stream v2.1.0: Issue on GitHub: "Add (javax.)Validation Support for Stream Listener"
I am trying to set up Cross Site Forging protection for my site that uses Spring MVC. My idea was to send a token in the HTML request header and verify it using AOP like this:
#Aspect
#Component
public class RequestMappingInterceptor {
#Before("execution(#org.springframework.web.bind.annotation.RequestMapping * *(..)) && args(request,..)")
public void before(JoinPoint point, HttpServletRequest request) throws Throwable {
UserEntity loggedUser = ((AmsUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getUserEntity();
String encodedToken = Base64.encodeBase64String(SessionEncodingUtils.encryptDecryptString(loggedUser.getId() + ";" + request.getSession(true).getId().hashCode()).getBytes());
if (!encodedToken.equals(request.getHeader("csrfToken"))) {
throw new RuntimeException("go.away");
}
}
}
However this does not work and i am not sure why. Shouldnt this intercept any method adnotated with #RequestMapping which contain a request argument? Any help would be appreciated
In my controllers, when I need the active (logged in) user, I am doing the following to get my UserDetails implementation:
User activeUser = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
log.debug(activeUser.getSomeCustomField());
It works fine, but I would think Spring could make life easier in a case like this. Is there a way to have the UserDetails autowired into either the controller or the method?
For example, something like:
public ModelAndView someRequestHandler(Principal principal) { ... }
But instead of getting the UsernamePasswordAuthenticationToken, I get a UserDetails instead?
I'm looking for an elegant solution. Any ideas?
Preamble: Since Spring-Security 3.2 there is a nice annotation #AuthenticationPrincipal described at the end of this answer. This is the best way to go when you use Spring-Security >= 3.2.
When you:
use an older version of Spring-Security,
need to load your custom User Object from the Database by some information (like the login or id) stored in the principal or
want to learn how a HandlerMethodArgumentResolver or WebArgumentResolver can solve this in an elegant way, or just want to an learn the background behind #AuthenticationPrincipal and AuthenticationPrincipalArgumentResolver (because it is based on a HandlerMethodArgumentResolver)
then keep on reading — else just use #AuthenticationPrincipal and thank to Rob Winch (Author of #AuthenticationPrincipal) and Lukas Schmelzeisen (for his answer).
(BTW: My answer is a bit older (January 2012), so it was Lukas Schmelzeisen that come up as the first one with the #AuthenticationPrincipal annotation solution base on Spring Security 3.2.)
Then you can use in your controller
public ModelAndView someRequestHandler(Principal principal) {
User activeUser = (User) ((Authentication) principal).getPrincipal();
...
}
That is ok if you need it once. But if you need it several times its ugly because it pollutes your controller with infrastructure details, that normally should be hidden by the framework.
So what you may really want is to have a controller like this:
public ModelAndView someRequestHandler(#ActiveUser User activeUser) {
...
}
Therefore you only need to implement a WebArgumentResolver. It has a method
Object resolveArgument(MethodParameter methodParameter,
NativeWebRequest webRequest)
throws Exception
That gets the web request (second parameter) and must return the User if its feels responsible for the method argument (the first parameter).
Since Spring 3.1 there is a new concept called HandlerMethodArgumentResolver. If you use Spring 3.1+ then you should use it. (It is described in the next section of this answer))
public class CurrentUserWebArgumentResolver implements WebArgumentResolver{
Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) {
if(methodParameter is for type User && methodParameter is annotated with #ActiveUser) {
Principal principal = webRequest.getUserPrincipal();
return (User) ((Authentication) principal).getPrincipal();
} else {
return WebArgumentResolver.UNRESOLVED;
}
}
}
You need to define the Custom Annotation -- You can skip it if every instance of User should always be taken from the security context, but is never a command object.
#Target(ElementType.PARAMETER)
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface ActiveUser {}
In the configuration you only need to add this:
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"
id="applicationConversionService">
<property name="customArgumentResolver">
<bean class="CurrentUserWebArgumentResolver"/>
</property>
</bean>
#See: Learn to customize Spring MVC #Controller method arguments
It should be noted that if you're using Spring 3.1, they recommend HandlerMethodArgumentResolver over WebArgumentResolver. - see comment by Jay
The same with HandlerMethodArgumentResolver for Spring 3.1+
public class CurrentUserHandlerMethodArgumentResolver
implements HandlerMethodArgumentResolver {
#Override
public boolean supportsParameter(MethodParameter methodParameter) {
return
methodParameter.getParameterAnnotation(ActiveUser.class) != null
&& methodParameter.getParameterType().equals(User.class);
}
#Override
public Object resolveArgument(MethodParameter methodParameter,
ModelAndViewContainer mavContainer,
NativeWebRequest webRequest,
WebDataBinderFactory binderFactory) throws Exception {
if (this.supportsParameter(methodParameter)) {
Principal principal = webRequest.getUserPrincipal();
return (User) ((Authentication) principal).getPrincipal();
} else {
return WebArgumentResolver.UNRESOLVED;
}
}
}
In the configuration, you need to add this
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="CurrentUserHandlerMethodArgumentResolver"/>
</mvc:argument-resolvers>
</mvc:annotation-driven>
#See Leveraging the Spring MVC 3.1 HandlerMethodArgumentResolver interface
Spring-Security 3.2 Solution
Spring Security 3.2 (do not confuse with Spring 3.2) has own build in solution: #AuthenticationPrincipal (org.springframework.security.web.bind.annotation.AuthenticationPrincipal) . This is nicely described in Lukas Schmelzeisen`s answer
It is just writing
ModelAndView someRequestHandler(#AuthenticationPrincipal User activeUser) {
...
}
To get this working you need to register the AuthenticationPrincipalArgumentResolver (org.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver) : either by "activating" #EnableWebMvcSecurity or by registering this bean within mvc:argument-resolvers - the same way I described it with may Spring 3.1 solution above.
#See Spring Security 3.2 Reference, Chapter 11.2. #AuthenticationPrincipal
Spring-Security 4.0 Solution
It works like the Spring 3.2 solution, but in Spring 4.0 the #AuthenticationPrincipal and AuthenticationPrincipalArgumentResolver was "moved" to an other package:
org.springframework.security.core.annotation.AuthenticationPrincipal
org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver
(But the old classes in its old packges still exists, so do not mix them!)
It is just writing
import org.springframework.security.core.annotation.AuthenticationPrincipal;
ModelAndView someRequestHandler(#AuthenticationPrincipal User activeUser) {
...
}
To get this working you need to register the (org.springframework.security.web.method.annotation.) AuthenticationPrincipalArgumentResolver : either by "activating" #EnableWebMvcSecurity or by registering this bean within mvc:argument-resolvers - the same way I described it with may Spring 3.1 solution above.
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver" />
</mvc:argument-resolvers>
</mvc:annotation-driven>
#See Spring Security 5.0 Reference, Chapter 39.3 #AuthenticationPrincipal
While Ralphs Answer provides an elegant solution, with Spring Security 3.2 you no longer need to implement your own ArgumentResolver.
If you have a UserDetails implementation CustomUser, you can just do this:
#RequestMapping("/messages/inbox")
public ModelAndView findMessagesForUser(#AuthenticationPrincipal CustomUser customUser) {
// .. find messages for this User and return them...
}
See Spring Security Documentation: #AuthenticationPrincipal
Spring Security is intended to work with other non-Spring frameworks, hence it is not tightly integrated with Spring MVC. Spring Security returns the Authentication object from the HttpServletRequest.getUserPrincipal() method by default so that's what you get as the principal. You can obtain your UserDetails object directly from this by using
UserDetails ud = ((Authentication)principal).getPrincipal()
Note also that the object types may vary depending on the authentication mechanism used (you may not get a UsernamePasswordAuthenticationToken, for example) and the Authentication doesn't strictly have to contain a UserDetails. It can be a string or any other type.
If you don't want to call SecurityContextHolder directly, the most elegant approach (which I would follow) is to inject your own custom security context accessor interface which is customized to match your needs and user object types. Create an interface, with the relevant methods, for example:
interface MySecurityAccessor {
MyUserDetails getCurrentUser();
// Other methods
}
You can then implement this by accessing the SecurityContextHolder in your standard implementation, thus decoupling your code from Spring Security entirely. Then inject this into the controllers which need access to security information or information on the current user.
The other main benefit is that it is easy to make simple implementations with fixed data for testing, without having to worry about populating thread-locals and so on.
Implement the HandlerInterceptor interface, and then inject the UserDetails into each request that has a Model, as follows:
#Component
public class UserInterceptor implements HandlerInterceptor {
....other methods not shown....
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
if(modelAndView != null){
modelAndView.addObject("user", (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal());
}
}
Starting with Spring Security version 3.2, the custom functionality that has been implemented by some of the older answers, exists out of the box in the form of the #AuthenticationPrincipal annotation that is backed by AuthenticationPrincipalArgumentResolver.
An simple example of it's use is:
#Controller
public class MyController {
#RequestMapping("/user/current/show")
public String show(#AuthenticationPrincipal CustomUser customUser) {
// do something with CustomUser
return "view";
}
}
CustomUser needs to be assignable from authentication.getPrincipal()
Here are the corresponding Javadocs of
AuthenticationPrincipal and AuthenticationPrincipalArgumentResolver
#Controller
public abstract class AbstractController {
#ModelAttribute("loggedUser")
public User getLoggedUser() {
return (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}
}
And if you need authorized user in templates (e.g. JSP) use
<%# taglib prefix="sec" uri="http://www.springframework.org/security/tags" %>
<sec:authentication property="principal.yourCustomField"/>
together with
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-taglibs</artifactId>
<version>${spring-security.version}</version>
</dependency>
You can try this:
By Using Authentication Object from Spring we can get User details from it in the controller method . Below is the example , by passing Authentication object in the controller method along with argument.Once user is authenticated the details are populated in the Authentication Object.
#GetMapping(value = "/mappingEndPoint") <ReturnType> methodName(Authentication auth) {
String userName = auth.getName();
return <ReturnType>;
}
To get the Active Users Details you can use #AuthenticationPrincipal in your controller like this:-
public String function(#AuthenticationPrincipal UserDetailsImpl user,
Model model){
model.addAttribute("username",user.getName()); //this user object
contains user details
return "";
}
UserDetailsImpl.java
import com.zoom.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.List;
public class UserDetailsImpl implements UserDetails {
#Autowired
private User user;
public UserDetailsImpl(User user) {
this.user = user;
}
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority("ADMIN");
return List.of(simpleGrantedAuthority);
}
#Override
public String getPassword() {
return user.getPassword();
}
#Override
public String getUsername() {
return user.getEmail();
}
#Override
public boolean isAccountNonExpired() {
return true;
}
#Override
public boolean isAccountNonLocked() {
return true;
}
#Override
public boolean isCredentialsNonExpired() {
return true;
}
#Override
public boolean isEnabled() {
return true;
}
public String getRole(){
return user.getRole();
}
public String getName(){
return user.getUsername();
}
public User getUser(){
return user;
}
}