I am using session service in AngularJS and #SessionAttributes in spring. Now how can we link both sessions and track the user session appropriately.
Below is my code:
#SessionAttributes("usersessionobj")
public class LoginController {
#RequestMapping(value = "/addStudent", method = RequestMethod.POST)
public String addStudent(#ModelAttribute("userModel") User user, ModelMap model) {
/*...*/
model.addAttribute("usersessionobj",userModel);
return "result";
}
}
#RequestMapping("/endsession")
public String nextHandlingMethod2(SessionStatus status){
status.setComplete();
return "login";
}
Related
I have a standard Hoemcontroller in ASP.NET Core MVC:
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public IActionResult Index(string user)
{
if(user != null)
{
TempData["UserName"] = user;
return View("Index", user);
}
return View("Index");
}
public IActionResult Privacy()
{
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
In the Index Action there will be a parameter sent from a winforms application. The string will contain the username of the client connecting to the website. This is the code for winforms:
public partial class Form1 : Form
{
public ChromiumWebBrowser chromeBrowser;
public Form1()
{
InitializeComponent();
InitializeChromium();
this.WindowState = FormWindowState.Maximized;
}
public void InitializeChromium()
{
CefSettings settings = new CefSettings();
Cef.Initialize(settings);
Cef.EnableHighDPISupport();
chromeBrowser = new ChromiumWebBrowser("https://localhost:5001/Home/Index/" + Environment.UserName);
this.Controls.Add(chromeBrowser);
//chromeBrowser.Dock = DockStyle.Fill;
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
Cef.Shutdown();
}
}
Now I am unsure on how to handle this on the webserver. I get the username inside the HomeController but in the same time when a user goes to the other pages with their controllers then the user should see only his content. Is that even possible?
It is not good practice to do authorization through a parameter in URI. For that, you should use Authentication (when the user passes his login and password) and receive a token with permissions. After that, you pass the token to the server and check permission there(using Authorize attribute for example). Example
If you are making a test project, and you don't need authentication at all, then you can pass a username everywhere you need and write some code to handle the content of every user (using headers, URI parameters, etc.)
I am working on Web API with AngularJS. I had implemented Web API token mechanism few days ago and able to login the application using the access token. I have used external DB table instead of ASP.NET identity table to authorize user.
I want to store user information in class so that it can be accessed easily from different controllers after User logged in. Currently I am using ClaimsIdentity in Controller Class to get the user information.
UserIdentityViewModel.cs
public class UserIdentityViewModel
{
public string UserName { get; set; }
public Guid UserId { get; set; }
}
Startup.cs
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
var myProvider = new AuthorizationServerProvider();
OAuthAuthorizationServerOptions options = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/Token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = myProvider
};
app.UseOAuthAuthorizationServer(options);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
}
}
AuthorizationServerProvider.cs
public class AuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
context.Validated(); //
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
string userId = context.UserName;
string password = context.Password;
EmployeeAccessBLL chkEmpAccessBLL = new EmployeeAccessBLL();
EmployeeAccessViewModel vmEmployeeAccess = chkEmpAccessBLL.CheckEmployeeAccess(Convert.ToInt32(userId), password);
if(vmEmployeeAccess != null)
{
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim("username", vmEmployeeAccess.EmpName));
identity.AddClaim(new Claim("userid", Convert.ToString(vmEmployeeAccess.EmployeeId)));
UserIdentityViewModel vmUser = new UserIdentityViewModel();
vmUser.UserId = vmEmployeeAccess.EmployeeId;
vmUser.UserName = vmEmployeeAccess.EmpName;
context.Validated(identity);
}
else
{
context.SetError("invalid_grant", "Provided username and password is incorrect");
return;
}
}
}
EventController.cs
public class StreamEventController : ApiController
{
[Authorize]
[Route("api/addevent")]
[HttpPost]
public List<string> AddEvent(StreamEventViewModel vmEvent)
{
//Able to get User Information from Identity.Claims
var identity = (ClaimsIdentity)User.Identity;
string userId = identity.Claims
.Where(c => c.Type == "userid")
.Select(c => c.Value).FirstOrDefault();
//Not able to get User Information from following as new object instance gets created
UserIdentityViewModel vmUser = new UserIdentityViewModel();
vmEvent.CreatedBy = vmUser.UserId;
vmEvent.ModifiedBy = vmUser.UserId;
}
}
Instead of writing "Identity.Claims" in each method of every controller I want to use simple get/set approach or any other methodology to get User Information . The use of Static class is also bad in my opinion as it will store one information of user and multiple user login information gets missed.
Please help me and share with me the best approach that has been used in other Web API projects for login.
You can add a private variable which will be set in the constructor of the controller, like this:
// Should only be used in protected methods.
private ClaimsIdentity ThisUser = null;
public MyController()
{
if (User.Identity.IsAuthenticated)
ThisUser = (ClaimsIdentity)User.Identity;
}
[Authorize]
[Route("api/addevent")]
[HttpPost]
public List<string> AddEvent(StreamEventViewModel vmEvent)
{
string userId = ThisUser.FindFirstValue("userid");
}
Or create a User class where you load all properties:
private UserClass ThisUser = null;
public MyController()
{
if (User.Identity.IsAuthenticated)
ThisUser = new UserClass(User);
}
[Authorize]
[Route("api/addevent")]
[HttpPost]
public List<string> AddEvent(StreamEventViewModel vmEvent)
{
string userId = ThisUser.UserId;
}
Where UserClass is something like:
public class UserClass
{
public string UserId { get; private set; }
public UserClass(IPrincipal user)
{
UserId = user.FindFirstValue("userid");
}
}
But this is just overhead for the same thing.
You can consider to move things to an extension. In that case you get something like:
public static class RequestExtensions
{
public static UserClass GetUser(this HttpRequestMessage request)
{
return new UserClass(request.GetOwinContext().Authentication.User);
}
public static ClaimsIdentiy GetUser2(this HttpRequestMessage request)
{
return new (ClaimsIdentity)request.GetOwinContext().Authentication.User;
}
}
Which you can call:
[Authorize]
[Route("api/addevent")]
[HttpPost]
public List<string> AddEvent(StreamEventViewModel vmEvent)
{
string userId = Request.GetUser.UserId;
string userId2 = Request.GetUser2.FindFirstValue("userid");
}
I think I would go for Request.GetUser2.FindFirstValue("userid");
The code is meant to give you an idea. I didn't test the code but I think it should work.
Use logged throuth the JSP form:
#ManagedBean
#SessionScoped
public class LoginView {
private String username; //+getter +setter
private String password; //+getter +setter
public String submit()
{
try {
HttpServletRequest request = (HttpServletRequest) FacesContext
.getCurrentInstance()
.getExternalContext()
.getRequest();
request.login(username, password);
} catch (ServletException e) {
FacesContext.getCurrentInstance().addMessage("login-form:username",
new FacesMessage(FacesMessage.SEVERITY_ERROR, "Validation Error", "Incorrect login or password"));
return "/login";
}
return "/index?faces-redirect=true";
}
}
How to check user logged in thorug EJB?
Below example whan I need:
#Stateless
public class SessionServiceBean {
#Resource
SessionContext sessionContext;
#EJB
UserService userService;
#Produces
#Named
#LoggedIn
public User getLoggedUser() {
if (/* check user is logged */) {
return userService.getByName(sessionContext.getCallerPrincipal().getName());
}
}
}
I foun only that the not logged use has name "anonymous", but it not better way, I think.
You are using a #Stateless EJB. In a Stateless EJB, you are working with user sessions. This is probably not right and the bean should be #Stateful.
I see no point in using EJBs in your example. Using pure CDI bean annotated with #SessionScoped would be sufficient.
You can store you user's session information directly in a session scoped bean. There is not need to use HttpServletRequest. For example:
#Named
#SessionScoped
public class UserSession implements Serializable {
private User activeUser;
public void logIn(User user) {
this.activeUser = user;
}
public void logOut() {
activeUser = null;
}
public boolean isLoggedIn() {
return activeUser != null;
}
public User getActiveUser() {
return activeUser;
}
}
I am using:
- Facebook-JavaScript-SDK (for login)
- Angular-JS (front end)
- Spring-Security (server security layer)
- Spring MVC (server MVC)
User's can log-into my web app by
(A) either using Facebook login (implemented in javascript FB SDK) OR
(B) via normal username/password form.
In case of (B)
i do a Form Post with fields:
j_username
j_password
to /j_spring_security_check (Spring Security)
This works fine.
But in case of (A), after the user has logged in via FB (in front-end),
What do i need to do to have Spring Security create the Session for the user?
All i have is facebook user's email with me at this point, and Server is unaware of this login in the front-end.
After successful login with Face Book,you should make AJAX request to your back-end API and make database entry about the Face book authentication for the user.
1)You need to implement UserDetailsService from Spring-Security
#Component
public class CustomUserDetailsService implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String userEmail) throws UsernameNotFoundException {
// check for Facebook authentication(DB Entry)
// fetch user details and roles from db using e-mail
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(roles);
UserDetails usd = new User(userId, userId, grantedAuths);
return usd;
}
}
2) Implement custom authentication provider
#Component("customAuthenticationProvider")
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Autowired
private CustomUserDetailsService customUserDetailsService;
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String userId = authentication.getName();
String password = authentication.getCredentials().toString();
UserDetails userFromDB = customUserDetailsService.loadUserByUsername(userId);
if (userFromDB != null) {
List<GrantedAuthority> grantedAuths = new ArrayList<GrantedAuthority>();
for (GrantedAuthority auth : userFromDB.getAuthorities()) {
grantedAuths.add(auth);
}
if (grantedAuths.size() != 0) {
System.out.println("user:" + userId + " got auth from custom auth provider");
return new UsernamePasswordAuthenticationToken(userFromDB, password, grantedAuths);
} else {
throw new DisabledException("No roles assigned");
}
}
throw new DisabledException("User or password incorrect");
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
3) Login controller
#Controller
public class LoginController {
#Autowired
private CustomAuthenticationProvider customAuthenticationProvider;
#Autowired
private CustomUserDetailsService customUserDetailsService;
#RequestMapping(value = "/login", )
public ResponseEntity<Object> fbLogin(#RequestParam String userEmail, HttpServletRequest request) {
UserDetails userDetails=customUserDetailsService.loadUserByUsername(userid);
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(userDetails, userid);
token.setDetails(new WebAuthenticationDetails(request));
Authentication authentication = this.customAuthenticationProvider.authenticate(token);
}
}
4) Spring Security Config
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomAuthenticationProvider authProvider;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider);
}
}
I am currently training and I'm working on Android application that uses RESTEasy API and I encounter some problem with ProxyFactory.create method (..., ...).
Let me explain it:
I have two REST services.
AuthenticateService :
#Path("/authent/tokens")
public interface AuthenticateService {
// This method add a data "token" in cookie
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public PostCustomerOutput createToken(PostCustomerInput postCustomerInput) throws ConnectException;
#Path("/{id}")
#DELETE
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Void deleteToken(#PathParam("id") String token);
}
EnrollmentService :
#Path("/enrollment/otp")
public interface UserEnrollmentService {
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public PostGenerateOTPOutput postGenerateOTP(PostGenerateOTPInput postGenerateOTPInput);
#POST
#Path("/check")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public OutputImpl postCheckOTP(PostCheckOTPInput postCheckOTPInput);
}
On these two services, I have a interceptor that handles recovered data in Cookies.
GrantAccessInterceptor :
public class GrantAccessInterceptor extends AbstractInDatabindingInterceptor {
public GrantAccessInterceptor() {
super(Phase.USER_STREAM);
}
#Override
public void handleMessage(Message message) throws Fault {
HttpServletRequest request = (HttpServletRequest) message.get(AbstractHTTPDestination.HTTP_REQUEST);
if (null != request) {
// Read http header to get cookie/
Cookie[] cookies = request.getCookies();
if (cookies != null) {
for (Cookie cook : cookies) {
if (cook.getName().equals("token")) {
log.info("Token find in cookies");
// TODO : do what I want with the cookie
}
}
} else {
log.info("Cookies are empty !");
}
}
}
}
Now I wrote the following test :
#org.junit.Test
public void testCreateToken() {
RegisterBuiltin.register(ResteasyProviderFactory.getInstance());
// Recover AuthenticateService
AuthenticateService authenticateService = ProxyFactory.create(AuthenticateService.class, urlLocal, executor);
// Recover UserEnrollmentService
UserEnrollmentService userEnrollmentService = ProxyFactory.create(UserEnrollmentService.class, urlLocal, executor);
PostCustomerInput in = new PostCustomerInput();
// put data in PostCustomerInput
PostCustomerOutput out = authenticateService.createToken(in);
// authenticateService.deleteToken(out.getCustomerToken());
PostGenerateOTPInput postGenerateOTPInput = new PostGenerateOTPInput();
userEnrollmentService.postGenerateOTP(postGenerateOTPInput);
}
When I call the method authenticateService.createToken, my GrantAccessInterceptor shows me the right message "Cookies are empty!" This is normal because the cookie is added to the createToken method.
Now, if I call deleteToken method on the same service (AuthenticateService) I get the message "Token find in cookies" which is OK.
Until then all is well.
Now, if after calling the method createToken of AuthenticateService I call a method of my UserEnrollmentService, GrantAccessInterceptor finds nothing in cookies ... -> "Cookies are empty!"
I think that the problem comes from ProxyFactory which does not share cookies between differents services.
It's not ProxyFactory's job to handle cookies, that's up to the ClientExecutor.
By passing the same ClientExecutor to ProxyFactory, you should be able to share cookies:
ApacheHttpClient4Executor executor = new ApacheHttpClient4Executor();
ProxyFactory.create(ServiceIntf1.class, "http://my-service-url", executor);
ProxyFactory.create(ServiceIntf1.class, "http://my-service-url", executor);