Thank you for your time.
To make it simple, I created a example service like below:
#RestController
#RequestMapping("/")
public class ComputeController {
#GetMapping("/add")
public int add(#RequestParam("left") int left, #RequestParam("right") int right) {
return left + right;
}
}
To protected this url, I config spring-security like this:
management.security.enabled=true
security.user.name=admin
security.user.password=admin
When I startup this service and access like this:
GET /add?left=100&right=11 HTTP/1.1
Authorization: ***** Hidden credentials *****
Host: localhost:7777
Connection: close
Everythis is going fine.
In other node, I created a "service-comsumer" by netflix feign. It's a Java Interface.
#FeignClient(name = "API-GATEWAY", path = "/compute-service", fallback = ComputeServiceCircuitBreaker.class)
public interface ComputeServiceClient {
#RequestMapping(path = "/add", method = RequestMethod.GET)
public Integer add(#RequestParam("left") Integer left, #RequestParam("right") Integer right);
}
But I DO NOT know how to config the request header "Authorization".
Any idea? Thanks again.
You need to create a FeignClient Configuration class, for example
import feign.auth.BasicAuthRequestInterceptor;
#Configuration
public class FeignClientConfiguration {
#Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
return new BasicAuthRequestInterceptor("admin", "admin");
}
}
then in your #FeignClient annotation use this configuration file:
#FeignClient(name="service", configuration = FeignClientConfiguration.class)
As of october 2020, this works:
public class FeignClientConfiguration {
#Bean
public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
return new BasicAuthRequestInterceptor("asdf", "asdf");
}
}
#FeignClient(name = "thirdPartyClient", url = "ceva.com",
configuration = FeignClientConfiguration.class)
public interface ThirdPartyClient {
#GetMapping
Response get();
}
Note, we don't annotate the configuration with #Configuration in order to not apply it to all requests.
Related
I know its possible to add a #PreAuthorize annotation to a Rest Controller...
#RestController
public class WebController {
#PreAuthorize("hasAuthority('Foo')")
#GetMapping("/restricted")
public ResponseEntity<String> restricted() {
return ResponseEntity.ok("Restricted section");
}
}
How can one preauthorize access to a Spring Integration Http.inbound gateway? I know I could add in a component to the Integration flow and add the annotation on a transformer or service activator method but I'd rather not have a separate object for that.
#Bean
//#PreAuthorize("hasAuthority('Foo')") ?
public HttpRequestHandlingMessagingGateway restrictedGateway() {
return Http.inboundGateway("/restricted")
...
.get();
}
#Bean
public IntegrationFlow myFlow(HttpRequestHandlingMessagingGateway restrictedGateway) {
return IntegrationFlows
.from(restrictedGateway)
.transform(source -> "Restricted section")
.get();
}
I think you are right by looking at https://docs.spring.io/spring-integration/reference/html/security.htm where it allows channel to be declared #Secured
Even if we think about spring security on a normal spring boot app without integration, it is at filter level so it seems to make sense as I consider HttpRequestHandlingMessagingGateway as a listener for http requests
Can you try
#Bean
#SecuredChannel(interceptor = "channelSecurityInterceptor", sendAccess = "ROLE_XXX")
public SubscribableChannel secureChannel() {
return new DirectChannel();
}
#Bean
public IntegrationFlow myFlow(HttpRequestHandlingMessagingGateway
restrictedGateway) {
return IntegrationFlows
.from(restrictedGateway)
.channel(secureChannel())
.transform(source -> "Restricted section")
.get();
}
I have a service inside an azure function
public MyService(
IConfigurationProvider configurationProvider,
ISerializationHelperService serializationHelperService,
ICommandListBuilder commandListBuilder,
[CosmosDB(
StaticSettings.Db,
StaticSettings.MyCollection.Collection,
ConnectionStringSetting = StaticSettings.DbConnectionStringSetting)] IDocumentClient documentClient)
{
//my logic here - this does get hit
}
My service is instantiated however, documentClient is null
How can I get this to be set properly? I dont get any errors
I have checked and there are no issues with the connection settings
public const string Db = "mydbname";
public const string DbConnectionStringSetting = "CosmosDBConnection";
public static class MyCollection
{
public const string Collection = "mycollectionname";
public static Uri CollectionUri => UriFactory.CreateDocumentCollectionUri(Db, Collection);
}
I am using a Startup class with an AddServices method to setup DI
Do I need to put something in there?
Paul
I have Azure function v2 project and I'm able to inject all my dependencies. Below lines added for IDocumentClient
string databaseEndPoint = Environment.GetEnvironmentVariable("DatabaseEndPoint");
string databaseKey = Environment.GetEnvironmentVariable("DatabaseKey");
builder.Services.AddSingleton<IDocumentClient>(new DocumentClient(new System.Uri(databaseEndPoint), databaseKey,
new ConnectionPolicy
{
ConnectionMode = ConnectionMode.Direct,
ConnectionProtocol = Protocol.Tcp,
RequestTimeout = TimeSpan.FromMinutes(5),//Groupasset sync has some timeout issue with large payload
// Customize retry options for Throttled requests
RetryOptions = new RetryOptions()
{
MaxRetryAttemptsOnThrottledRequests = 5,
MaxRetryWaitTimeInSeconds = 60
}
}
));
My Database Service
protected readonly IDocumentClient client;
protected BaseDao(IDocumentClient client)
{
this.client = client;
}
hope it will help!
I am implementing a custom owin middleware to add CSP header (content security policy) in response header. For CSP to work, the middleware needs to create a unique nonce value per request. So i have NonceService which creates nonce value. The custom OWIN middleware has dependency on NonceService.
However my problem is i'm not able to register custom middleware per request.
When i debug i noticed OWIN is NOT creating new instance of custom middleware per request and because of that same nonce value is being used for all the requests.
Below is my code
Nonce Service
public interface INonceService
{
string GetNonce();
}
public class NonceService : INonceService
{
private static readonly RNGCryptoServiceProvider _rng = new RNGCryptoServiceProvider();
private readonly string _nonce;
public NonceService(int nonceByteAmount = 32)
{
var nonceBytes = new byte[nonceByteAmount];
_rng.GetBytes(nonceBytes);
_nonce = Convert.ToBase64String(nonceBytes);
}
public string GetNonce()
{
return _nonce;
}
}
OWIN Middleware & Extension
public class SecurityHeaderMiddleware : OwinMiddleware
{
private readonly INonceService _nonceService = null;
public SecurityHeaderMiddleware(OwinMiddleware next, INonceService nonceService) : base(next)
{
_nonceService = nonceService;
}
public override async Task Invoke(IOwinContext context)
{
await Next.Invoke(context);
var nonce = _nonceService.GetNonce();
var csp = BuildCSPHeader(nonce);
context.Response.Headers.Add(key, csp);
}
}
public static class OwinExtensions
{
private const string SecurityHeaderRegistrationKey = "SecurityHeaders";
public static IAppBuilder UseSecurityHeader(this IAppBuilder app, INonceService nonceService)
{
if (app == null)
throw new ArgumentNullException("app");
if (app.Properties.ContainsKey(SecurityHeaderRegistrationKey))
return app;
app.Use<SecurityHeaderMiddleware>(nonceService);
app.Properties.Add(SecurityHeaderRegistrationKey, true);
return app;
}
}
I am using Unity as conatiner, so i register INonceService using PerRequestLifetimeManager
container.RegisterType<INonceService, NonceService>(new PerRequestLifetimeManager(), new InjectionConstructor(32));
and i register my custom middleware with OWIN in startup.cs
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
var nonceService = ServiceLocator.Current.GetInstance<INonceService>();
app.UseSecurityHeader(nonceService);
}
}
Note
If creating per request OWIN middleware is not possible then at least how should i pass per request new instance of NonceService to middleware.
Looking at the stack trace upon a null reference exception for HttpContext that I received, it seems that middleware is constructed in BuildWebHost from Main.
I'm using Unity too, so I changed my constructor dependency from...
IUserRepository userRepository
to
Func<IUserRepository> userRepositoryFactory
and my method call from...
userRepository.FetchLoggedInUser(userIdentifier);
to
userRepositoryFactory().FetchLoggedInUser(userIdentifier);
Conveniently, Unity automatically generates a factory, so there's no need to edit the registrations.
My Spring Boot Application is secured by Spring Security OAuth2. The userdata is stored in a SQL-database. I followed here royclarkson's Oauth protected REST service. This project works with Spring Data JPA. This works fine.
https://github.com/royclarkson/spring-rest-service-oauth
But now I want to implement my Neo4J Configuration to get data from my Neo4J-Database via Neo4J-JDBC (JDBC-template). Here I followed this GitHub project:
https://github.com/neo4j-examples/movies-java-spring-boot-jdbc
As a standalone application it works, but if I put this two projects togehter, I get this Exception:
HibernateJpaAutoConfiguration.class]: Invocation of init method failed;
nested exception is org.hibernate.HibernateException:
Unable to determine Dialect to use [name=Neo4j, majorVersion=3];
user must register resolver or explicitly set 'hibernate.dialect'
My Neo4jConfig.java looks like this:
#Configuration
public class Neo4jConfig {
//NEO4J Server Implementation via JDBC
private static final String NEO4J_URL = System.getProperty("NEO4J_URL","jdbc:neo4j://localhost:7474");
private static final String NEO4J_USER = System.getProperty("NEO4J_USER","neo4j");
private static final String NEO4J_PASSWORD = System.getProperty("NEO4J_PASSWORD","neo4j");
#Bean
public DataSource dataSource() {
return new DriverManagerDataSource(NEO4J_URL, NEO4J_USER, NEO4J_PASSWORD);
}
public Neo4jConfig(){
}
public String getNeo4JURL(){
return NEO4J_URL;
}
}
TripController.java
import hello.data.Trip;
#RestController
public class TripController {
#Autowired
JdbcTemplate template;
public static final RowMapper<Trip> TRIP_ROW_MAPPER = new RowMapper<Trip>() {
public Trip mapRow(ResultSet rs, int rowNum) throws SQLException {
return new Trip(rs.getString("tripname"),rs.getInt("slots"), rs.getInt("to_date"), rs.getInt("from_date"));
}
};
String SEARCH_TRIPS_QUERY =
" MATCH (t:Trip)\n" +
" RETURN t.tripname as tripname, t.slots as slots, t.to_date as to_date, t.from_date as from_date";
#RequestMapping(path = "/alltrips", method = RequestMethod.GET)
public List<Trip> alltrips() {
return template.query(SEARCH_TRIPS_QUERY, TRIP_ROW_MAPPER);
}
}
I hope you guys understand my question. I know, I am a really newone at Spring, but I hope anyone can help me :)
This is happening because hibernate does not find any dialect for Neo4J as Neo4j is not RDBMS database and dialect is not provided by default. You can use Hibernate OGM (search and include it in pom.xml), and then use following configuration to configure Entitymanager and Transaction manager
#Configuration
#EnableJpaRepositories(basePackages = {
"your repository packages" }, entityManagerFactoryRef = "n4jEntityManager", transactionManagerRef = "n4jTxnManager")
public class DatabaseConfiguration {
#Bean(name = "n4jEntityManager")
public LocalContainerEntityManagerFactoryBean entityManager() {
Map<String, Object> properties = new HashMap<String, Object>();
properties.put("javax.persistence.transactionType", "resource_local");
properties.put("hibernate.ogm.datastore.provider","neo4j");
properties.put("hibernate.ogm.datastore.host","localhost");
properties.put("hibernate.ogm.datastore.port","7474");
properties.put("hibernate.ogm.datastore.database", "your database");
properties.put("hibernate.ogm.datastore.create_database", "true or false");
LocalContainerEntityManagerFactoryBean entityManager = new LocalContainerEntityManagerFactoryBean();
entityManager.setPackagesToScan("your domain packages");
entityManager.setPersistenceUnitName("n4jPU");
entityManager.setJpaPropertyMap(properties);
entityManager.setPersistenceProviderClass(HibernateOgmPersistence.class);
return entityManager;
}
#Bean(name = "n4jTxnManager")
public PlatformTransactionManager txnManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(mongoEntityManager().getObject());
return transactionManager;
}
}
But I suggest, to remove Hibernate altogether if you are not going to use RDBMS and will only be using Neo4j. Spring data has good support for NoSQL databases and Entities can be defined using annotations like #NodeEntity and #GraphId
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.