DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

Curious about the future of data-driven systems? Join our Data Engineering roundtable and learn how to build scalable data platforms.

Data Engineering: The industry has come a long way from organizing unstructured data to adopting today's modern data pipelines. See how.

Threat Detection: Learn core practices for managing security risks and vulnerabilities in your organization — don't regret those threats!

Managing API integrations: Assess your use case and needs — plus learn patterns for the design, build, and maintenance of your integrations.

Avatar

Bojan Tomić

Back-end development ninja at Leangen

Townsville, NO

Joined Nov 2006

http://leangen.io

About

Bojan Tomić is an experienced Java developer with a healthy interest in modern approaches to old problems, be it in the form of programming paradigms (like functional-reactive), or innovative methods for creating APIs (like GraphQL). Finds immense pleasure in sampling craft beers, playing RPGs, both computer and table-top, and spending all his money on traveling (often off the beaten path). And he's an all-around nice guy to boot ;)

Stats

Reputation: 345
Pageviews: 22.4K
Articles: 1
Comments: 12
  • Articles
  • Comments

Articles

article thumbnail
Spring Security Run-As example using annotations and namespace configuration
Spring Security offers an authentication replacement feature, often referred to as Run-As, that can replace the current user's authentication (and thus permissions) during a single secured object invocation. Using this feature makes sense when a backend system invoked during request processing requires different privileges than the current application. For example, an application might want to expose a financial transaction log to the currently logged in user, but the backend system that provides it only permits this action to the members of a special "auditor" role. The application can not simply assign this role to the user as that would potentially permit them to execute other restricted actions. Instead, the user can be given this right exclusively for viewing their transaction log. Only two classes are used to implement this feature. Instances of RunAsManager are tasked with producing the actual replacement authentication tokens. A sensible default implementation is already provided by Spring Security. As with other types of authentication, it is also necessary to register an instance of an appropriate AuthenticationProvider. Tokens produced by runAsManager are signed with the provided key (my_run_as_key in the example above) and are later checked against the same key by runAsAuthenticationProvider, in order to mitigate the risk of fake tokens being provided. These keys can have any value, but need to be the same in both objects. Otherwise, runAsAuthenticationProvider will reject the produced tokens as invalid. If an instance is registered, RunAsManager will be invoked by AbstractSecurityInterceptor for every intercepted object invocation for which the user has already been given access. If RunAsManager returns a token, this token will be used be used instead of the original one for the duration of the invocation, thus granting the user different privileges. There are two key points here. In order for the authentication replacement feature to do anything, the call has to actually be secured (and thus intercepted), and the user has to already have been granted access. To register a RunAsManager instance with the method security interceptor, something similar to the following is needed: Now, all methods secured by the @Secured annotation will be able to trigger RunAsManager. One important point here is that global-method-security will only work in the Spring context in which it is defined. In Spring MVC applications, there usually are two Spring contexts: the parent context, attached to ContextLoaderListener, and the child context, attached toDispatcherServlet. To secure Controller methods in this way, global-method-security must be added to DispatcherServlet's context. To secure methods in beans not in this context, global-method-security should also be added to ContextLoaderListener's context. Otherwise, security annotations will be ignored. The default implementation of RunAsManager (RunAsManagerImpl) will inspect the secured object's configuration and if it finds any attributes prefixed with RUN_AS_, it will create a token identical to the original, with the addition of one new GrantedAuthorty per RUN_AS_ attribute found. The new GrantedAuthority will be a role (prefixed by ROLE_ by default) named like the found attribute without the RUN_AS_ prefix. So, if a user with a role ROLE_REGISTERED_USER invokes a method annotated with @Secured({"ROLE_REGISTERED_USER","RUN_AS_AUDITOR"}), e.g. @Controller public class TransactionLogController { @Secured({"ROLE_REGISTERED_USER","RUN_AS_AUDITOR"}) //Authorities needed for method access and authorities added by RunAsManager prefixed with RUN_AS_ @RequestMapping(value = "/transactions", method = RequestMethod.GET) //Spring MVC configuration. Not related to security @ResponseBody //Spring MVC configuration. Not related to security public List getTransactionLog(...) { ... //Invoke something in the backend requiring ROLE_AUDITOR { ... //User does not have ROLE_AUDITOR here } the resulting token created by RunAsManagerImpl with be granted ROLE_REGISTERED_USER and ROLE_AUDITOR. Thus, the user will also be allowed actions, normally reserved for ROLE_AUDITOR members, during the current invocation, permitting them, in this case, to access the transaction log.To enable runAsAuthenticationProvider, register it as usual: ... other authentication-providers used by the application ... This is all that is necessary to have the default implementation activated. Still, this setting will not work for methods secured by @PreAuthorize and @PostAuthorize annotations as their configuration attributes are differently evaluated (they are SpEL expressions and not a simple list or required authorities like with @Secured) and will not be recognized by RunAsManagerImpl. For this scenario to work, a custom RunAsManager implementation is required, as, at least at the time of writing, no applicable implementation is provided by Spring. A custom RunAsManager implementation for use with @PreAuthorize/@PostAuthorize A convenient implementation relying on a custom annotation is provided below: public class AnnotationDrivenRunAsManager extends RunAsManagerImpl { @Override public Authentication buildRunAs(Authentication authentication, Object object, Collection attributes) { if(!(object instanceof ReflectiveMethodInvocation) || ((ReflectiveMethodInvocation)object).getMethod().getAnnotation(RunAsRole.class) == null) { return super.buildRunAs(authentication, object, attributes); } String roleName = ((ReflectiveMethodInvocation)object).getMethod().getAnnotation(RunAsRole.class).value(); if (roleName == null || roleName.isEmpty()) { return null; } GrantedAuthority runAsAuthority = new SimpleGrantedAuthority(roleName); List newAuthorities = new ArrayList(); // Add existing authorities newAuthorities.addAll(authentication.getAuthorities()); // Add the new run-as authority newAuthorities.add(runAsAuthority); return new RunAsUserToken(getKey(), authentication.getPrincipal(), authentication.getCredentials(), newAuthorities, authentication.getClass()); } } This implementation will look for a custom @RunAsRole annotation on a protected method (e.g. @RunAsRole("ROLE_AUDITOR")) and, if found, will add the given authority (ROLE_AUDITOR in this case) to the list of granted authorities. RunAsRole itself is just a simple custom annotation: @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface RunAsRole { String value(); } This new implementation would be instantiated in the same way as before: And registered in a similar fashion: The expression-handler is always required for pre-post-annotations to work. It is a part of the standard Spring Security configuration, and not related to the topic described here. Both pre-post-annotations and secured-annotations can be enabled at the same time, but should never be used in the same class. The protected controller method from above could now look like this: @Controller public class TransactionLogController { @PreAuthorize("hasRole('ROLE_REGISTERED_USER')") //Authority needed to access the method @RunAsRole("ROLE_AUDITOR") //Authority added by RunAsManager @RequestMapping(value = "/transactions", method = RequestMethod.GET) //Spring MVC configuration. Not related to security @ResponseBody //Spring MVC configuration. Not related to security public List getTransactionLog(...) { ... //Invoke something in the backend requiring ROLE_AUDITOR { ... //User does not have ROLE_AUDITOR here }
July 7, 2014
· 22,383 Views · 1 Like

Comments

5 Hidden Secrets in Java

Feb 24, 2018 · Justin Albano

If you really have a need to instantiate annotation, use a library that implements that correctly. Doing what was described in the article can easily lead to obscure bugs, like equal annotations not being equal etc.

GeAnTyRef (my project) has a correct implementation: https://github.com/leangen/geantyref/blob/master/README.md#creating-annotated-types-dynamically-using-typefactory

Kotlin: Reified Type Parameters

Jan 29, 2018 · Mike Gates

... wow

Mastering Reactive Streams (Part 1): Publisher

Jan 28, 2018 · Oleh Dokuka

Brilliant article! Have you ever written part 2?

Java 8 Lambda Limitations: Closures

Feb 21, 2017 · Miro Mannino

Was wondering the same. But nice article, nevertheless.

A Functional Approach to Given When Then Using Java 8

Jan 03, 2017 · Gabriel Deliu

Woud you mind explaining what benefits it brings compared to labels/comments? Seems like the wrapping functions are just shoved in there because everytone is doing it.

What's Wrong with Java 8: Currying vs Closures

Oct 25, 2016 · Pierre-Yves Saumont

I could do with less moaning about Java's percieved deficiencies, but otherwsie, a brilliant article! Explains a non obvious topic using good examples and providing rationale - rarely seen on DZone these days.

The Elements of Modern Java Style

Oct 17, 2016 · Mike Gates

Just use the Google Java Styleguide. It already has prepared settings files for every IDE and is the most complete and up-to-date of all Java styleguides.

9 Things in JDK 9 That Aren’t Jigsaw

Oct 17, 2016 · Mike Gates

The good ones, yes. That's always been the way: other JVM languages go and experiment and Java, being the slow-moving enterprise langauge by design, picks up the features proved good. This is a very smart dynamic, if you ask me, as it gives everyone what they're in for. Plus, don't forget JDK9 is about Jigsaw (which has nothing to do with Scala), everything else is trinkets in comparison.

Forbidden APIs of Java

Sep 12, 2016 · Duncan Brown

Good point about never relying on defaults when it comes to locales and encodings. But

what if English character sets change in the future

Seriously? It's like future-proofing for physics changing.

Adding automatic updates to your program - Part 1

Jul 06, 2014 · Tony Thomas

Can someone please explain to me what has Neo4j to do with browsers and web workers? Isn't Neo4j a database? Is this article maybe about some other related product that I don't know of?
Whitepapers on .NET Framework 3.5

Dec 04, 2011 · Tony Thomas

I don't work on the client side so all this is a bit alien to me, but your idea sounds quite good in my ears. Transactions, cursors and foreign keys belong in the back end. The front end needs a place to shove JSON (as you noted) and similar blobs of data.

Hm, when I think about it a bit, I think I'm looking at this from a different standpoint from you but I still agree in the end.

Java NIO vs. IO

Sep 11, 2011 · James Sugrue

I don't think

while(! bufferFull(bytesRead) ) { bytesRead = inChannel.read(buffer); }
was a very good example. It basically blocked an otherwise non-blocking channel.

User has been successfully modified

Failed to modify user

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends: