in my web application I have many places where I need to retrieve current logged account (from session).
Account account = getAccountFromSession(session);
This piece of code is repeated many many times. I wonder if this is possible to make my request look something like this:
#RequestMapping(value="/something", method=RequestMethod.POST)
public String handleSomething(
#RequestParam String someParam,
#Account Account account) { ... }
where #Account is my custom annotation.
How can I tell spring to process this annotation?
Use HandlerMethodArgumentResolver (or WebArgumentResolver for versions below 3.1).
To enable your argument resolver:
If you use #EnableWebMvc - make your #Configuration implement WebMvcConfigurer and override addArgumentResolvers()
If you use <mvc:annotation-driven> - use argument-resolvers attribute
For older versions you may need to declare AnnotationMethodHandlerAdapter manually and set its customArgumentResolvers
Related
I have a REST service, written using Spring MVC. The server is an OAuth2 resource server and I am using the JwtAuthenticationProvider to have the JWT parsed and turned into the Principal. This all works fine.
However, what I really want to do is to load user details from a database, using the username provided from a Claim in the JWT. Then that new Principal should replace or (ideally) wrap the Jwt so that it is available directly from the SecurityContext.
I am really struggling to see how to do this. The JwtAuthenticationProvider does not seem to work with a UserDetailsService. I also looked at doing this with a Converter - but it is not easy to extend JwtAuthenticationConverter because the convert method is final (why?).
So to be very clear, here is what I ideally want to happen:
Bearer token is presented to service.
Parse Jwt and extract claims
Use one of these claims as a key to my user database, where I can look up attributes, entitlements etc
Turn these into a new Principal object which is available in the SecurityContext's Authentication object.
The configure method in my WebSecurityConfigurerAdapter has this:
http.authorizeRequests().antMatchers("/api/*").authenticated().and().oauth2ResourceServer().jwt();
I cannot be the only person who wants to use a user database along with OAuth2, so I must be missing something fundamental? I am using Spring Security 5.2.0.
The JwtAuthenticationProvider does not support an UserDetailsService because in theory you are not supposed to have UserDetails in an application that does not manage credentials. I'm not saying that you cannot have any type of user, but the UserDetailsService will not be used or autowired by Spring Security.
You could register a bean of type JwtAuthenticationConverter, do whatever you need in the convert method and return your custom authentication token, like so:
#Component
public class JwtAuthenticationConverterAdapter implements Converter<Jwt, AbstractAuthenticationToken> {
private JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
#Override
public AbstractAuthenticationToken convert(Jwt jwt) {
var token = this.jwtAuthenticationConverter.convert(jwt);
// build your custom token with the properties from the token above
return customToken;
}
}
I agree with your concerns - and I have found it useful to override Spring's default processing. There is a claims extensibility pattern I use with some providers, where JWT handling is only one part.
I have a Spring Boot code sample that you can run - it uses a custom filter and Connect2Id classes - OAuth integration is described here. Happy to answer any follow up questions if it helps
I have been able to use #PostFilter / #PostAuthorize successfully as follows:
#RestController
public class MyController{
#PostFilter("hasPermission(filterObject, 'blah')")
public List<MyObject> getIt();
}
However, what I would like to do is configure the hasPermission (or some similar) check on all endpoints in my application. In other words, doing something like a Spring Security Fitler but using the domain object prior to it being marshalled in the HttpServletResponse. I would prefer not to use a Filter because of the marshalling / unmarshalling aspect especially when doing filtering (#PostFilter) as it changes the result.
The idea is that I would like this filtering behavior to be a cross-cutting security concern and therefore not have to remember to add it to each endpoint as it is added. This would also allow me to do testing of this filtering once for the application instead of for each endpoint.
I'm trying to convert a Stripes web app to Grails. The Stripes app uses Spring Security, but I would like the Grails app to use the Spring Security Grails plugin.
The app already has User and Role (Java) classes that I need to reuse, i.e. I cannot use the Grails domain classes that the s2-quickstart script generates.
The Spring Security plugin docs describe how to use an existing User domain class. The steps seem to be:
define a UserDetails implementation that reads from the existing User domain class
define a custom UserDetailsService implementation that returns instances of (1)
register an instance of (2) as a Spring bean named userDetailsService.
However the docs don't provide any information about how to use an existing Role class and the class that represents the many-to-many relationship between User and Role.
What other steps are necessary to use existing Role, User, and UserRole classes with the Grails Spring Security plugin? Is there any reason for me to run the s2-quickstart script if I don't want to generate any domain classes?
Follow-Up Questions to Burt's Answer
In the end, what you need is a new GrailsUser
Presumably GrailsUser here refers to the custom UserDetails implementation? In my case I'll probably just implement the interface directly. Does something like this seem reasonable?
class UserAdapter implements UserDetails {
private String password
private Collection<GrantedAuthority> springRoles
UserAdapter(User user) {
this.password = user.password
Collection<Role> roles = // load legacy Role objects
this.springRoles = roles.collect { new GrantedAuthorityImpl(it.authority) }
}
// If using password hashing, presumably this is the hashed password?
String getPassword() {
password
}
///////// other UserDetails methods omitted
Collection<GrantedAuthority> getAuthorities() {
springRoles
}
}
I'm not storing the whole User object within UserAdapter because of your warning about storing a potentially large object in the HTTP session.
what you need is.....and a List of GrantedAuthority instances (and the id if it's a GrailsUser)
If I use my own UserDetails implementation as above, then presumably I can ignore this comment about providing an id?
Finally, if I follow the approach outlined above, should I set these properties in Config.groovy and do I need to run the s2-quickstart script (or any others)?
Keep in mind that Spring Security doesn't care where the data comes from, it just needs a UserDetails instance when authenticating with the DAO auth provider and it can come from anywhere. It's convenient to use domain classes and database tables, but it's just one approach. Do what works for your data. In the end, what you need is a new GrailsUser (or some other impl) instance with the username and password set, the 3 booleans set, and a List of GrantedAuthority instances (and the id if it's a GrailsUser).
The simplest thing to do when you have legacy user and role data is to create a custom UserDetailsService. Use GORM, raw SQL queries, whatever you need to get the required data.
Another option is to write your own AuthenticationProvider like Glen did here: http://blogs.bytecode.com.au/glen/2010/01/15/hacking-custom-authentication-providers-with-grails-spring-security.html - although that's a larger solution that also involves a custom filter which you wouldn't need. The DAO provider uses a UserDetailsService but it's fine to create your own that combines the functionality into one class.
It's not a good idea to reuse your User domain class as the UserDetails though. Even if you implement the interface, you'd be storing a disconnected potentially large (if there are attached collections) object in the HTTP session. The POJO/POGO implementations (Spring Security's User class, the plugin's GrailsUser class, etc.) are very small and just a few Strings and booleans.
within the config.groovy file you have to specify your domain classes to use:
grails.plugins.springsecurity.userLookup.userDomainClassName = 'your.package.User'
grails.plugins.springsecurity.userLookup.authorityJoinClassName = 'your.package.UserRole'
grails.plugins.springsecurity.authority.className = 'your.package.Role'
i thinks it's not neccessary to implement your own userDetail service, because spring security uses
SpringSecurityUtils.securityConfig.userLookup
method to determine the domain class you configured before. your domain classes must provide the required fields and relations.
I've been doing some comparisons between Apache Shiro and Spring Security - I'm really loving the security model that Shiro uses and believe it to be far cleaner that Spring Security.
However, one big nice-to-have would be to be able to reference method parameters from within the method-level security annotations. For example, right now I could so something like:
#RequiresPermissions("account:send:*")
public void sendEmail( EmailAccount account, String to, String subject, String message) { ... }
Within the context of this example, this means that the authenticated user must have the permission to send emails on email accounts.
However, this is not fine-grained enough, as I want instance level permissions! In this context, assume that users can have permissions on instances of email accounts. So, I'd like to write the previous code something like this:
#RequiresPermissions("account:send:${account.id}")
public void sendEmail( EmailAccount account, String to, String subject, String message) { ... }
In this way, the permission string is referencing a parameter passed into the method such that the method can be secured against a particular instance of EmailAccount.
I know I could easily do this from plain Java code within the method, but it would be great to achieve the same thing using annotations - I know Spring Security supports Spring EL expressions in its annotations.
Is this definitely not a feature of Shiro and thus will I have to write my own custom annotations?
Thanks,
Andrew
Look at the classes in http://shiro.apache.org/static/current/apidocs/org/apache/shiro/authz/aop/package-summary.html, especially PermissionAnnotationHandler. There you can see that all Shiro does when encountering the #RequiresPermissions annotation is call getSubject().isPermitted(permission) and does no substitution inside the annotation value at all. You would have to somehow override that handler if you wanted this kind of functionality.
So to answer your question: yes, this is definitely not a feature of Shiro and you have to either write your own annotation or somehow override that handler.
This feature is currently not supported by Shiro. Multiple people have requested this feature. Perhaps we can vote for the issue?
https://issues.apache.org/jira/browse/SHIRO-484
https://issues.apache.org/jira/browse/SHIRO-77
https://issues.apache.org/jira/browse/SHIRO-417
https://issues.apache.org/jira/browse/SHIRO-331
I have a JIRA plugin that I'm developing that has a REST service. That service should be able to accept POSTed requests, unmarshall some data and store it. The seemingly suggested way to do this in JIRA is to make use of the Bandana persistence framework. According to this page, I should be able to simply define a setter that Spring should call to give me my Bandana manager.
#Path("/path")
public class SCMService {
private BandanaManager bandanaManager;
// setter called by Spring
public void setBandanaManager(BandanaManager bandanaManager) {
this.bandanaManager = bandanaManager;
}
//...More methods...
}
However, when I test this, the setter is never being called and my manager is null. I'm guessing this should be as simple as registering this service with Spring for injection somehow but I can't seem to find anything like that.
How would I get my setter called? Is there a better way to do this?
Er, I'm not sure that JIRA uses Bandana in that way, though Confluence does. You can certainly post data to a JIRA rest resource and then store it using properties tables
Something like this:
#POST
#Consumes (MediaType.APPLICATION_XML)
public Response createComponentAndIssues(#Context HttpServletRequest request, ...