In Spring Web I used #PreAuthorize with SpEl to check permission of current user. Something like that:
#Component
public class CustomSecurity {
public boolean checkPermission(){
Authentication authentication = SecurityContextHolder.getContext()
.getAuthentication();
CurrentAccount currentAccount = (CurrentAccount)authentication.getPrincipal();
return currentAccount.getUsername().equals("admin");
}
}
In RestController:
#PreAuthorize("#customSecurity.checkPermission()")
#GetMapping("/")
public Object getWidgetValues() {
return "Hello admin";
}
Now I try to use WebFlux.
Wrote reactiveCheckPermission.
public boolean checkPermission2() {
return ReactiveSecurityContextHolder.getContext()
.map(SecurityContext::getAuthentication)
.map(Authentication::getPrincipal)
.map(o -> (CurrentAccount) o)
.map(currentAccount -> currentAccount.getUsername().equals("admin"))
.block();
}
But it throws IllegalStateException("block()/blockFirst()/blockLast() are blocking, which is not supported in thread parallel
Changed boolean to Mono, but #PreAuthroze needs only boolean, not Mono.
How to use #PreAuthorize in WebFlux right?
I found one solution.
#PreAuthorize("#customSecurity.checkPermission(#account)")
#GetMapping("/")
public Object getWidgetValues(#AuthenticationPrincipal(expression = "account") Account account) {
return "Hello admin";
}
Where CurrentAccount used in ReactiveUserDetailsService
public class CurrentAccount extends User {
private Account account;
public CurrentAccount(Account account) {
super(account.getLogin(), account.getPassword(), true, true,
true, !account.isLocked(),
AuthorityUtils.createAuthorityList(account.getRole().name()));
this.account = account;
}
if you want to do that more manually, you can:
#GetMapping("/resources")
public Flux<Object> getResources() {
return ReactiveSecurityContextHolder.getContext()
.map(SecurityContext::getAuthentication)
.map(Authentication::getPrincipal)
.map(o -> (CurrentAccount) o)
.filter(currentAccount ->
currentAccount.getUsername().equals("admin"))
.switchIfEmpty(Mono.error(new ResponseStatusException(FORBIDDEN)))
.then(<here further processing>);
}
Related
How can we use #AuthenticationPrincipal with a RSocket Method #AuthenticationPrincipal Mono token
public Mono<String> uppercase(String s, #AuthenticationPrincipal Mono<JwtAuthenticationToken> token) {
//Token is always null
return Mono.just(s.toUpperCase());
}
I created a RSocketSecurityConfiguration class:
#Configuration
#EnableRSocketSecurity
#EnableReactiveMethodSecurity
#Slf4j
public class RSocketSecurityConfiguration {
#Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
private String issuerUri;
#Bean
PayloadSocketAcceptorInterceptor rsocketInterceptor(RSocketSecurity rsocket) {
rsocket
.authorizePayload(authorize ->
authorize
.anyRequest().authenticated()
.anyExchange().permitAll()
)
.jwt(jwtSpec -> {
jwtSpec.authenticationManager(jwtReactiveAuthenticationManager(reactiveJwtDecoder()));
});
return rsocket.build();
}
#Bean
ReactiveJwtDecoder reactiveJwtDecoder() {
NimbusReactiveJwtDecoder decoder = (NimbusReactiveJwtDecoder)
ReactiveJwtDecoders.fromOidcIssuerLocation(issuerUri);
return decoder;
}
#Bean
public JwtReactiveAuthenticationManager jwtReactiveAuthenticationManager(ReactiveJwtDecoder reactiveJwtDecoder) {
JwtReactiveAuthenticationManager jwtReactiveAuthenticationManager = new JwtReactiveAuthenticationManager(reactiveJwtDecoder);
JwtAuthenticationConverter authenticationConverter = new JwtAuthenticationConverter();
JwtGrantedAuthoritiesConverter jwtGrantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
jwtGrantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");
authenticationConverter.setJwtGrantedAuthoritiesConverter(jwtGrantedAuthoritiesConverter);
jwtReactiveAuthenticationManager.setJwtAuthenticationConverter( new ReactiveJwtAuthenticationConverterAdapter(authenticationConverter));
return jwtReactiveAuthenticationManager;
}
#Bean
RSocketMessageHandler messageHandler(RSocketStrategies strategies) {
RSocketMessageHandler mh = new RSocketMessageHandler();
mh.getArgumentResolverConfigurer().addCustomResolver(new AuthenticationPrincipalArgumentResolver());
mh.setRSocketStrategies(strategies);
return mh;
}
Full UpperCaseController:
#Slf4j
#Controller
public class UpperCaseController {
#MessageMapping("uppercase")
public Mono<String> uppercase(String s, #AuthenticationPrincipal Mono<JwtAuthenticationToken> token) {
JwtAuthenticationToken currentToken = token.block();
if ( currentToken == null ) {
log.info("token is null");
}
return Mono.just(s.toUpperCase());
}
}
Full ConnectController:
#Slf4j
#Controller
public class ConnectController {
#ConnectMapping("connect")
void connectShellClientAndAskForTelemetry(RSocketRequester requester,
#Payload String client) {
requester.rsocket()
.onClose()
.doFirst(() -> {
// Add all new clients to a client list
log.info("Client: {} CONNECTED.", client);
})
.doOnError(error -> {
// Warn when channels are closed by clients
log.warn("Channel to client {} CLOSED", client);
})
.doFinally(consumer -> {
// Remove disconnected clients from the client list
log.info("Client {} DISCONNECTED", client);
})
.subscribe();
}
}
RSocket Client:
#Component
#Slf4j
public class RSocketClient {
private static final MimeType SIMPLE_AUTH = MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString());
MimeType BEARER_AUTH =
MimeTypeUtils.parseMimeType(WellKnownMimeType.MESSAGE_RSOCKET_AUTHENTICATION.getString());
private static final String BEARER_TOKEN = "....";
private final RSocketRequester requester;
private RSocketStrategies rsocketStrategies;
public RSocketClient(RSocketRequester.Builder requesterBuilder,
#Qualifier("rSocketStrategies") RSocketStrategies strategies) {
this.rsocketStrategies = strategies;
SocketAcceptor responder = RSocketMessageHandler.responder(rsocketStrategies, new RSocketClientHandler());
requester = requesterBuilder
.setupRoute("connect")
.setupData("MyTestClient")
.setupMetadata(new BearerTokenMetadata(BEARER_TOKEN), BEARER_AUTH)
.rsocketStrategies(builder ->
builder.encoder(new BearerTokenAuthenticationEncoder()))
.rsocketConnector(connector -> connector.acceptor(responder))
.connectTcp("localhost", 7000)
.block();
requester.rsocket()
.onClose()
.doOnError(error -> log.warn("Connection CLOSED"))
.doFinally(consumer -> log.info("Client DISCONNECTED"))
.subscribe();
}
public void uppercase() {
String response = requester
.route("uppercase")
.metadata(BEARER_TOKEN, BEARER_AUTH)
.data("Hello")
.retrieveMono(String.class).block();
log.info(response);
}
}
I have done something very similar for Spring REST and it works fine but for RSocket the token is always null.
I assume you have started with https://spring.io/blog/2020/06/17/getting-started-with-rsocket-spring-security
I was able to get this working for my codebase using a different type than #Payload
#ConnectMapping
fun handle(requester: RSocketRequester, #AuthenticationPrincipal jwt: String) {
logger.debug("connected $jwt")
}
#MessageMapping("runCommand")
suspend fun runCommand(request: CommandRequest, rSocketRequester: RSocketRequester, #AuthenticationPrincipal jwt: String): Flow<CommandResponse> {
...
}
I am using Keycloak as jwt Issuer. Just follow up this git repo. Only thing that didn't work for me is
#CurrentUserProfile Mono<UserProfile> currentUserProfile
Solution for that w.r.t above git repo will be using either of the below
#CurrentUserProfile Mono<Jwt> currentUserProfile
or directly use
#AuthenticationPrincipal Jwt currentUserProfile
Hope this will work for you. Enjoy!
I have an intranet application that uses the Windows username and passes that to a procedure to return data.
I'm using dependency injection, but I don't believe I have the method to get the username separated properly.
I'm trying to keep this secure by not passing in the username as a parameter, but I also want to be able to impersonate (or bypass my GetWindowsUser() method) and send in another username so I can test results for other users.
One idea I had for this was to set a session variable in another page with another (impersonated) username, then check if that session variable exists first before grabbing the actual user name, but I couldn't figure out how to access the session variable in the repository.
WEB API CONTROLLER
public class DropDownDataController : ApiController
{
private IDropDownDataRepository _dropDownDataRepository;
//Dependency Injection using Unity.WebAPI NuGet Package
public DropDownDataController(IDropDownDataRepository dropDownDataRepository)
{
_dropDownDataRepository = dropDownDataRepository;
}
[HttpGet]
public HttpResponseMessage MyList()
{
try
{
return _dropDownDataRepository.MyList();
}
catch
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
}
}
}
REPOSITORY
public class DropDownDataRepository : IDropDownDataRepository, IDisposable
{
private DatabaseEntities db = new DatabaseEntities();
public HttpResponseMessage MyList()
{
//(This should be separated somehow, right?)
//Create a new instance of the Utility class
Utility utility = new Utility();
//Grab the windowsUser from the method
var windowsUser = utility.GetWindowsUser();
//Pass windowsUser parameter to the procedure
var sourceQuery = (from p in db.myProcedure(windowsUser)
select p).ToList();
string result = JsonConvert.SerializeObject(sourceQuery);
var response = new HttpResponseMessage();
response.Content = new StringContent(result, System.Text.Encoding.Unicode, "application/json");
return response;
}
}
INTERFACE
public interface IDropDownDataRepository : IDisposable
{
HttpResponseMessage MyList();
}
UTILITY CLASS
public class Utility
{
public string GetWindowsUser()
{
//Get the current windows user
string windowsUser = HttpContext.Current.User.Identity.Name;
return windowsUser;
}
}
UPDATE 1
In addition to what Nikolai and Brendt posted below, the following is also needed to allow Web Api controllers work with the session state.
Accessing Session Using ASP.NET Web API
Abstract the Utility class and inject it into the repository.
Then you can stub or mock for testing.
public interface IUtility
{
string GetWindowsUser();
}
public class TestUtility : IUtility
{
public string GetWindowsUser()
{
return "TestUser";
}
}
public class DropDownDataRepository : IDropDownDataRepository, IDisposable
{
private IUtility _utility;
public DropDownDataRepository(IUtility utility)
{
_utility = utility;
}
}
EDIT
Also the repository should not return an HTTPResponseMessage type it should just return a List<T> of the domain model you're accessing.
i.e.
public List<Model> MyList()
{
//Grab the windowsUser from the method
var windowsUser = _utility.GetWindowsUser();
//Pass windowsUser parameter to the procedure
var sourceQuery = (from p in db.myProcedure(windowsUser)
select p).ToList();
return sourceQuery
}
Then move the JSON portion to the controller.
One idea I had for this was to set a session variable in another page
with another (impersonated) username, then check if that session
variable exists first before grabbing the actual user name, but I
couldn't figure out how to access the session variable in the
repository.
Potentially, if you add in a dependency to session, you need to isolate it, e.g.
public class DropDownDataRepository : IDropDownDataRepository, IDisposable
{
// ... other fields
private ISession session;
public DropDownDataRepository(ISession session)
{
this.session = session;
}
public HttpResponseMessage MyList()
{
var myUserName = this.session.UserName;
// ... etc
With ISession being something like:
public interface ISession
{
string UserName { get; }
}
Implemented as:
public class MySession : ISession
{
public string UserName
{
get
{
// potentially do some validation and return a sensible default if not present in session
return HttpContext.Current.Session["UserName"].ToString();
}
}
}
Of course there is the potential to decouple this MySession class from HttpContext if desired.
With regards to this:
//(This should be separated somehow, right?)
//Create a new instance of the Utility class
Utility utility = new Utility();
Yes, anytime you create a new object you are tightly coupling them together, which will give you issues, for example, if you try to unit test it in isolation.
In this instance you could extract an IUtility interface from Utility:
public class Utility : IUtility
{
string GetWindowsUser();
}
Then:
public class DropDownDataRepository : IDropDownDataRepository, IDisposable
{
// ... other fields
private IUtility utility;
public DropDownDataRepository(IUtility utility)
{
this.utility = utility;
// .... etc
Then you have removed the depenedency between Utility and DropDownDataRepository, and can substitute in another type or mock with ease.
I got a lot of help from Nikolai and Brent and got most of the way there with their posted answers, but ended up figuring out the complete answer on my own. The problems I was having were related to not being able to access session variables in a WebAPI. So, I'm sure there are cleaner solutions to this, but I definitely improved what I had and came up with the following code, which works.
This answer was needed to allow access to the session variable in Web Api - Accessing Session Using ASP.NET Web API
GLOBAL.asax.cs
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
GlobalConfiguration.Configure(WebApiConfig.Register);
UnityConfig.RegisterComponents();
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
//Added to allow use of session state in Web API
protected void Application_PostAuthorizeRequest()
{
if (IsWebApiRequest())
{
HttpContext.Current.SetSessionStateBehavior(SessionStateBehavior.Required);
}
}
//Added to allow use of session state in Web API
private bool IsWebApiRequest()
{
return HttpContext.Current.Request.AppRelativeCurrentExecutionFilePath.StartsWith(WebApiConfig.UrlPrefixRelative);
}
protected void Session_Start(Object sender, EventArgs e)
{
//Default set the session variable to none
Session["_impersonatedUser"] = "none";
}
protected void Session_End(Object sender, EventArgs e)
{
//Reset the session variable to blank
Session["_impersonatedUser"] = "";
}
}
UNITY.config
public static class UnityConfig
{
public static void RegisterComponents()
{
var container = new UnityContainer();
// register all your components with the container here
// it is NOT necessary to register your controllers
// e.g. container.RegisterType<ITestService, TestService>();
container.RegisterType<IDropDownDataRepository, DropDownDataRepository>();
container.RegisterType<IUtilityRepository, UtilityRepository>();
container.RegisterType<ISessionRepository, SessionRepository>();
//MVC5
//Unity.MVC5 NuGet Package
DependencyResolver.SetResolver(new Unity.Mvc5.UnityDependencyResolver(container));
//WEB API
//Unity.WebApi NuGet Package
GlobalConfiguration.Configuration.DependencyResolver = new Unity.WebApi.UnityDependencyResolver(container);
}
}
WEB API CONTROLLER
public class DropDownDataController : ApiController
{
private IDropDownDataRepository _dropDownDataRepository;
//Dependency Injection using Unity.WebAPI NuGet Package
public DropDownDataController(IDropDownDataRepository dropDownDataRepository)
{
_dropDownDataRepository = dropDownDataRepository;
}
[HttpGet]
public HttpResponseMessage MyList()
{
try
{
var sourceQuery = _dropDownDataRepository.MyList();
//JSON stuff moved to controller
string result = JsonConvert.SerializeObject(sourceQuery);
var response = new HttpResponseMessage();
response.Content = new StringContent(result, System.Text.Encoding.Unicode, "application/json");
return response;
}
catch
{
throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.NotFound));
}
}
protected override void Dispose(bool disposing)
{
_dropDownDataRepository.Dispose();
base.Dispose(disposing);
}
}
DROPDOWNDATA REPOSITORY
public class DropDownDataRepository : IDropDownDataRepository, IDisposable
{
private DatabaseEntities db = new DatabaseEntities();
private IUtilityRepository _utilityRepository;
private ISessionRepository _sessionRepository;
//Dependency Injection of Utility and Session
public DropDownDataRepository(IUtilityRepository utilityRepository, ISessionRepository sessionRepository)
{
_utilityRepository = utilityRepository;
_sessionRepository = sessionRepository;
}
//Changed to a list here
public List<MyProcedure> MyList()
{
string windowsUser;
//Check the session variable to see if a user is being impersonated
string impersonatedUser = _sessionRepository.ImpersonatedUser;
//Grab the windowsUser from the Utility Repository
windowsUser = _utilityRepository.GetWindowsUser();
if (impersonatedUser != "none")
{
windowsUser = impersonatedUser;
}
//Pass windowsUser parameter to the procedure
var sourceQuery = (from p in db.MyProcedure(windowsUser)
select p).ToList();
return sourceQuery;
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
db.Dispose();
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
DROPDOWNDATA INTERFACE
public interface IDropDownDataRepository : IDisposable
{
//Changed to list here
List<MyProcedure> MyList();
}
UTILITY REPOSITORY
public class UtilityRepository : IUtilityRepository
{
public string GetWindowsUser()
{
//Get the current windows user
string windowsUser = HttpContext.Current.User.Identity.Name;
return windowsUser;
}
}
UTILITY INTERFACE
public interface IUtilityRepository
{
string GetWindowsUser();
}
SESSION REPOSITORY
public class SessionRepository : ISessionRepository
{
public string ImpersonatedUser
{
get
{
return HttpContext.Current.Session["_impersonatedUser"].ToString();
}
}
}
SESSION INTERFACE
public interface ISessionRepository
{
string ImpersonatedUser { get; }
}
How do I convert the following Ninject DI to the equivalent for LightInject DI? I'm having issues with getting to the right syntax.
Database.SetInitializer(new MigrateDatabaseToLatestVersion<DefaultMembershipRebootDatabase, BrockAllen.MembershipReboot.Ef.Migrations.Configuration>());
kernel.Bind<UserAccountService>().ToSelf();
kernel.Bind<AuthenticationService>().To<SamAuthenticationService>();
kernel.Bind<IUserAccountQuery>().To<DefaultUserAccountRepository>().InRequestScope();
kernel.Bind<IUserAccountRepository>().To<DefaultUserAccountRepository>().InRequestScope();
On my original question, I didn't include this, but this (also posted as comment to this post) was the not working code I attempted to make it work:
Database.SetInitializer(new MigrateDatabaseToLatestVersion<DefaultMembershipRebootDatabase, BrockAllen.MembershipReboot.Ef.Migrations.Configuration>());
container.Register<UserAccountService>();
container.Register<AuthenticationService, SamAuthenticationService>();
container.Register<IUserAccountQuery, DefaultUserAccountRepository>(new PerRequestLifeTime());
container.Register<IUserAccountRepository, DefaultUserAccountRepository>(new PerRequestLifeTime());
The error message (without the stack trace) given was this:
Exception Details: System.InvalidOperationException: Unresolved dependency [Target Type: BrockAllen.MembershipReboot.Ef.DefaultUserAccountRepository], [Parameter: ctx(BrockAllen.MembershipReboot.Ef.DefaultMembershipRebootDatabase)], [Requested dependency: ServiceType:BrockAllen.MembershipReboot.Ef.DefaultMembershipRebootDatabase, ServiceName:]
Source Error:
An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.
*If anyone wants to see the stack trace too - * just ask, and I'll post it in a reply to this question.
The constructor for DefaultMembershipRebootDatabase (as was in a sample project, my project used the dll provided through nuget, and the constructor wasn't available, but I'm pretty sure they're more than likely the same in both cases (seeing as how it comes from the same source...) is:
public class DefaultMembershipRebootDatabase : MembershipRebootDbContext<RelationalUserAccount>
{
public DefaultMembershipRebootDatabase()
: base()
{
}
public DefaultMembershipRebootDatabase(string nameOrConnectionString)
: base(nameOrConnectionString)
{
}
public DefaultMembershipRebootDatabase(string nameOrConnectionString, string schemaName)
: base(nameOrConnectionString, schemaName)
{
}
}
This is the constructor (as was in the same aforementioned sample project) for the DefaultUserAccountRepository:
public class DefaultUserAccountRepository
: DbContextUserAccountRepository<DefaultMembershipRebootDatabase, RelationalUserAccount>,
IUserAccountRepository
{
public DefaultUserAccountRepository(DefaultMembershipRebootDatabase ctx)
: base(ctx)
{
}
IUserAccountRepository<RelationalUserAccount> This { get { return (IUserAccountRepository<RelationalUserAccount>)this; } }
public new UserAccount Create()
{
return This.Create();
}
public void Add(UserAccount item)
{
This.Add((RelationalUserAccount)item);
}
public void Remove(UserAccount item)
{
This.Remove((RelationalUserAccount)item);
}
public void Update(UserAccount item)
{
This.Update((RelationalUserAccount)item);
}
public new UserAccount GetByID(System.Guid id)
{
return This.GetByID(id);
}
public new UserAccount GetByUsername(string username)
{
return This.GetByUsername(username);
}
UserAccount IUserAccountRepository<UserAccount>.GetByUsername(string tenant, string username)
{
return This.GetByUsername(tenant, username);
}
public new UserAccount GetByEmail(string tenant, string email)
{
return This.GetByEmail(tenant, email);
}
public new UserAccount GetByMobilePhone(string tenant, string phone)
{
return This.GetByMobilePhone(tenant, phone);
}
public new UserAccount GetByVerificationKey(string key)
{
return This.GetByVerificationKey(key);
}
public new UserAccount GetByLinkedAccount(string tenant, string provider, string id)
{
return This.GetByLinkedAccount(tenant, provider, id);
}
public new UserAccount GetByCertificate(string tenant, string thumbprint)
{
return This.GetByCertificate(tenant, thumbprint);
}
}
And this is the controller in my project:
namespace brockallen_MembershipReboot.Controllers
{
using System.ComponentModel.DataAnnotations;
using BrockAllen.MembershipReboot;
using BrockAllen.MembershipReboot.Mvc.Areas.UserAccount.Models;
public class UserAccountController : Controller
{
UserAccountService _userAccountService;
AuthenticationService _authService;
public UserAccountController(AuthenticationService authService)
{
_userAccountService = authService.UserAccountService;
_authService = authService;
}
// GET: /UserAccount/
[Authorize]
public ActionResult Index()
{
return View();
}
public ActionResult Login()
{
return View(new LoginInputModel());
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginInputModel model)
{
if (ModelState.IsValid)
{
/*BrockAllen.MembershipReboot.*/UserAccount account;
if (_userAccountService.AuthenticateWithUsernameOrEmail(model.Username, model.Password, out account))
{
_authService.SignIn(account, model.RememberMe);
_authService.SignIn(account, model.RememberMe);
/*if (account.RequiresTwoFactorAuthCodeToSignIn())
{
return RedirectToAction("TwoFactorAuthCodeLogin");
}
if (account.RequiresTwoFactorCertificateToSignIn())
{
return RedirectToAction("CertificateLogin");
}
if (_userAccountService.IsPasswordExpired(account))
{
return RedirectToAction("Index", "ChangePassword");
}*/
if (Url.IsLocalUrl(model.ReturnUrl))
{
return Redirect(model.ReturnUrl);
}
return RedirectToAction("Index");
}
else
{
ModelState.AddModelError("", "Invalid Username or Password");
}
}
return View(model);
}
public ActionResult Register()
{
return View(new RegisterInputModel());
}
[ValidateAntiForgeryToken]
[HttpPost]
public ActionResult Register(RegisterInputModel model)
{
if (ModelState.IsValid)
{
try
{
var account = _userAccountService.CreateAccount(model.Username, model.Password, model.Email);
ViewData["RequireAccountVerification"] = _userAccountService.Configuration.RequireAccountVerification;
return View("Success", model);
}
catch (ValidationException ex)
{
ModelState.AddModelError("", ex.Message);
}
}
return View(model);
}
}
}
The constructor for AuthenicationService is:
public abstract class AuthenticationService : AuthenticationService<UserAccount>
{
public new UserAccountService UserAccountService
{
get { return (UserAccountService)base.UserAccountService; }
set { base.UserAccountService = value; }
}
public AuthenticationService(UserAccountService userService)
: this(userService, null)
{
}
public AuthenticationService(UserAccountService userService, ClaimsAuthenticationManager claimsAuthenticationManager)
: base(userService, claimsAuthenticationManager)
{
}
}
By default, LightInject does not resolve concrete classes without registering them, while NInject does.
For example, NInject can resolve DefaultMembershipRebootDatabase without registering it, while LightInject cannot by default. Take a look at this.
In any way, to fix your issue, make sure that you register your concrete classes (that are needed as dependencies in other classes). Here is an example:
container.Register<DefaultMembershipRebootDatabase>();
I am assuming here that some class has a dependency on the concrete class DefaultMembershipRebootDatabase. If you have other concrete class dependencies, make sure that you also register them.
You should use the PerScopeLifetime rather than the PerRequestLifetime. PerRequestLifetime represents a transient lifetime that tracks disposable instances and disposes them when the scope ends. PerScopeLifetime ensures the same instance within a scope which in this case means the same instance within a web request.
How to generate "intercept-url" dynamically. My user name and roles are stored in database,
I want to map all these users in to spring security.Is there any way to do this?
You'll have to provide your own implementation of com.icod.solapCore.spring.security.FilterInvocationSecurityMetadataSource.
This could look like this :
public class MyFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
#Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
FilterInvocation filterInvocation = (FilterInvocation) object;
HttpServletRequest request = filterInvocation.getHttpRequest();
Collection<ConfigAttribute> result = new ArrayList<ConfigAttribute>();
// Find roles in database that secures the specified request
// ...
// For any role found, create a SecurityConfig object prefixed with "ROLE_" ex :
// for(String role : roles) {
// ConfigAttribute attribute = new SecurityConfig("ROLE_"+roleFound);
// result.add(attribute);
// }
return result;
}
#Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
#Override
public boolean supports(Class<?> clazz) {
return FilterInvocation.class.isAssignableFrom(clazz);
}
}
And then you'll have to replace the default FilterInvocationSecurityMetadataSource with your own. I do it with a BeanPostProcessor, called after spring read the configuration file but before it makes the configuration official. Looks like this :
public class MyFilterInvocationSecurityMetadataSourceBeanPostProcessor implements BeanPostProcessor {
private FilterInvocationSecurityMetadataSource metadataSource = new MyFilterInvocationSecurityMetadataSource();
#Override
public Object postProcessBeforeInitialization(Object bean, String name) throws BeansException {
if (bean instanceof FilterInvocationSecurityMetadataSource) {
return metadataSource;
}
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String name) throws BeansException {
return bean;
}
}
Then you just have to configure the bean post processor :
<bean id="solapcoreFilterInvocationSecurityMetadataSourceBeanPostProcessor" class="foo.bar.MyFilterInvocationSecurityMetadataSourceBeanPostProcessor"/>
Hope this help.
Give all your users same role and operate with this role in config.
You can read abour roles here
i give users special URL with access key in it. users accessing the public page via this special url should be able to see some additional data as compared to simple anonymous user.
i want to give some additional role to anonymous user based on parameters provided in request so i can do something like this in my template:
<#sec.authorize ifAnyGranted="ROLE_ADMIN, ROLE_USER, ROLE_INVITED_VISITOR">
...some additional stuff for invited user to see
</#sec.authorize>
currently i'm implementing Spring's OncePerRequestfilter:
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
if (null != request.getParameter("accessKey")) {
if(isValid(request.getParameter("accessKey"))) {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
//how do i add additional roles to authenticated (potentially anonymous) user?
}
}
}
Why not just create a wrapper class that delegates to the original, but adds on a couple of extra GrantedAuthorities:
public class AuthenticationWrapper implements Authentication
{
private Authentication original;
private GrantedAuthority[] extraRoles;
public AuthenticationWrapper( Authentication original, GrantedAuthority[] extraRoles )
{
this.original = original;
this.extraRoles = extraRoles;
}
public GrantedAuthority[] getAuthorities()
{
GrantedAuthority[] originalRoles = original.getAuthorities();
GrantedAuthority[] roles = new GrantedAuthority[originalRoles.length + extraRoles.length];
System.arraycopy( originalRoles, 0, roles, 0, originalRoles.length );
System.arraycopy( extraRoles, 0, roles, originalRoles.length, extraRoles.length );
return roles;
}
public String getName() { return original.getName(); }
public Object getCredentials() { return original.getCredentials(); }
public Object getDetails() { return original.getDetails(); }
public Object getPrincipal() { return original.getPrincipal(); }
public boolean isAuthenticated() { return original.isAuthenticated(); }
public void setAuthenticated( boolean isAuthenticated ) throws IllegalArgumentException
{
original.setAuthenticated( isAuthenticated );
}
}
and then do this in your filter:
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
GrantedAuthority extraRoles = new GrantedAuthority[2];
extraRoles[0] = new GrantedAuthorityImpl( "Role X" );
extraRoles[1] = new GrantedAuthorityImpl( "Role Y" );
AuthenticationWrapper wrapper = new AuthenticationWrapper( auth, extraRoles );
SecurityContextHolder.getContext().setAuthentication( wrapper );
The Authentication is now replaced by your version with the extra roles. NB You may have to handle the case where the Authentication has not yet been authenticated and so its getAuthorities() returns null. (The wrapper implementation currently assumes that it will always get a non-null array from its wrapped Authentication)