Hi
I am currently playing with Guice and #SessionScoped. To give it more sense, I decided to build a (very simple) authentication process.
Below, I will explain each step I have done. Then I will ask you some questions.
[1] I have create an Identity class which represents a person (guest or user) :
#SessionScoped
public class Identity implements Serializable
{
private String uid;
private String name;
public boolean isAuthenticate()
{
return uid != null;
}
public void logout()
{
this.uid = null;
}
/*Setters-Getters*/
}
[2] Next, I created an Authentication class that log-in user:
public class Authentication
{
#Override
public Identity authenticate(String login, String password)
{
/*some code*/
Identity identity = new Identity();
identity.setUid(user.getId());
return identity;
}
}
[3] Then, in my Servlet, I log-in the user :
#RequestScoped
public class LoginAction
{
#Inject
Injector injector;
protected void login(HttpServletRequest req, HttpServletResponse resp)
{
Identity identity = injector.getInstance(Identity.class);
Authentication auth = new Authentication();
identity = auth.authenticate("login","password");
}
}
[4] Finally, I create a Filter that show me if user is authenticated :
#Singleton
public class SecurityFilter implements Filter
{
#Inject
private Injector injector;
#Override
public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain)
{
Identity identity = injector.getInstance(Identity.class);
if(identity.isAuthenticate())
{
System.err.println("USER");
}
else
{
System.err.println("GUEST");
}
chain.doFilter(request, response);
}
}
Well, this code is not working. My Identity's uid is always "null".
Let's go for questions :
a - First of all, Why did my code not works ?
b - Is #SessionScoped equivalent to set the object in HttpSession ?
c - How to invalidate the Identity object (only it) in (http)session ?
d - Generally, In which case did we have to use #SessionScoped?
Thanks you for reading,
Waiting your answers.
[a] You're assigning a new instance of Identity to a local variable in LoginAction, not replacing the instance managed by Guice. You could solve the problem by populating the uid and name fields on the existing Identity instance managed by Guice.
For example, instead of
identity = auth.authenticate("login","password");
you could say:
Identity identity = injector.getInstance(Identity.class);
Authentication auth = new Authentication();
Identity authenticated = auth.authenticate("login","password");
identity.setUid(authenticated.getUid());
identity.setName(authenticated.getName());
There are cleaner ways to do it, but you get the idea.
[b]/[d] That's correct: #SessionScoped is equivalent to setting a variable in the HttpSession, and this is the kind of situation that you would use it. You'll need it for objects that need to be unique across sessions, but need to be available for every request.
[c] I'm not quite sure what you mean, but if you're wanting to redirect to different places in the app depending on whether the user is logged in, your filter design is a common way to do that.
Some improvements that you could make:
Have a SessionScoped service that manages the session's user's Identity, and make sure it's synchronized on the Identity instance. That way you won't have concurrency troubles if a user makes two requests in quick succession.
Prefer injecting Providers instead of injecting the Injector(examples here) to decouple your classes from Guice.
Inject dependencies into your classes' constructors, instead of injecting fields. This allows for easier testing (by providing mock/stub dependencies in tests).
Related
I use Spring Security, and I found strange behavior of framework while login. Spring Security WebAuthenticationDetails has parameter sessionId which is getting from HTTP request, and it all should be good, but in fact REST request gives me another session id. If I will autowire HttpSession and then get session id from it, I will get Spring-like id. So it seems that I have two ids for one user. Is it correct? Or I missed something?
EDITED:
For example this class will gave some session id
public class AuthenticationEventListener implements ApplicationListener<AbstractAuthenticationEvent> {
#Autowired
HttpSession httpSession;
#Override
public void onApplicationEvent(AbstractAuthenticationEvent event) {
if (event instanceof AuthenticationSuccessEvent) {
LoggedUser loggedUser = (LoggedUser) event.getAuthentication().getPrincipal();
loggedUser.initSessionParams(event.getAuthentication());
String sessionId = httpSession.getId();
}
}
}
and this method will give another one:
#RequestMapping(value = "/chart")
public Map getTestStatusesChart(HttpServletRequest request) {
String sessionId= request.getSession(false).getId();
return null;
}
So the answer is next: with condition of security Spring change session id by default. To prevent such behavior you need to disable session-fixation-protection in Spring Security config. more info by link
I want to do my own implementation of Bearer-only authentication in Wildfly. In essence, I will do the following steps:
When I receive a request, I will check if it has an Authorization header.
I obtain the token and check against a database (in this case I will be using Redis) for the validity of it.
I obtain the role for that user from the database.
I want to be able to use the #RolesAllowed annotation on my rest services.
How do I go about it? How do I need to modify the Wildfly configuration files? What interfaces do I need to implement? How can I pass the role of the user to the security context so that Wildfly does the #RolesAllowed check for me?
If answering, consider that I am an experienced Java Programmer, but new to Wildfly, so you can skip details on programming logic but not on Wildfly configuration. Also in your answer don't worry on how the token got to Redis in the first place, or how the client obtained it.
EDIT
This is what I have done, but with no luck yet. I have implemented an AuthenticationFilter that implements ContainerRequestFilter. (I am including below only the main filter function that I have implemented. Note that there are some helper functions that get the roles from the database that are not included). Even when, at the end of the function I set the security context of the request context with the user profile (which contains the role), I cannot get to work the #RolesAllowed annotations on my JAX-RS rest services. Any pointers on what should I do?
Note: I have not modified any Wildfly configuration files or the web.xml file. I know that the filter is being called for every request because I am able to LOG messages from it on every request.
/**
* (non-Javadoc)
* #see javax.ws.rs.container.ContainerRequestFilter#filter(javax.ws.rs.container.ContainerRequestContext)
*/
#Override
public void filter(ContainerRequestContext requestContext) {
//1. Read the JSON web token from the header
String authorizationHeader = requestContext.getHeaderString(HttpHeaders.AUTHORIZATION);
if (authorizationHeader == null || !authorizationHeader.startsWith("Bearer ")) {
requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());
return;
}
String token = authorizationHeader.substring("Bearer".length()).trim();
try{
//Note that if the token is not in the database,
//an exception will be thrown and we abort.
UserProfile userProfile = this.getUserProfile(token);
if (null == userProfile){
userProfile = this.decodeToken(token);
}
if (null == userProfile){
throw new Exception();
}
String role = userProfile.getUserRole();
if (null == role){
role = this.getRoleFromMod(userProfile);
if (null == role){
role = RoleType.READ_ONLY;
}
userProfile.setUserRole(role);
this.updateUserProfileForToken(token, userProfile);
}
userProfile.setUserRole(role);
//5. Create a security context class that implements the crazy interface
//and set it here.
requestContext.setSecurityContext(new ModSecurityContext(userProfile));
}
catch(Exception e){
requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());
}
}
Yeah I am not sure how it would work in an EE environment, even making the resource class an stateless bean. The #RolesAllowed annotation is meant to be used for ejbs. In which case the principal is retrieved from the servlet request (I believe). What I would do is just implements your own authorization filter that looks up the annotation and checks against the principal in the security context.
You can see how Jersey implements it. Nothing is really Jersey specific about it except the AnnotatedMethod class. For that you can just do some reflection with java.lang.reflect.Method (resourceInfo.getResourceMethod()) instead. Other than that, you can pretty much copy the code as is. Once you're done, just register the RolesAllowedDynamicFeature with the application. Or just annotate it with #Provider to be scanned for.
You will also need to make sure your authentication filter is annotated with #Priority(Priorities.AUTHENTICATION) so that it is called before the authorization filter, which is annotated with #Priority(Priorities.AUTHORIZATION).
UPDATE
Here is a refactor of the the code I linked to, so It doesn't use an Jersey specific classes. The AnnotatedMethod is just changed to Method.
#Provider
public class RolesAllowedFeature implements DynamicFeature {
#Override
public void configure(ResourceInfo resourceInfo, FeatureContext configuration) {
Method resourceMethod = resourceInfo.getResourceMethod();
if (resourceMethod.isAnnotationPresent(DenyAll.class)) {
configuration.register(new RolesAllowedRequestFilter());
return;
}
RolesAllowed ra = resourceMethod.getAnnotation(RolesAllowed.class);
if (ra != null) {
configuration.register(new RolesAllowedRequestFilter(ra.value()));
return;
}
if (resourceMethod.isAnnotationPresent(PermitAll.class)) {
return;
}
ra = resourceInfo.getResourceClass().getAnnotation(RolesAllowed.class);
if (ra != null) {
configuration.register(new RolesAllowedRequestFilter(ra.value()));
}
}
#Priority(Priorities.AUTHORIZATION) // authorization filter - should go after any authentication filters
private static class RolesAllowedRequestFilter implements ContainerRequestFilter {
private final boolean denyAll;
private final String[] rolesAllowed;
RolesAllowedRequestFilter() {
this.denyAll = true;
this.rolesAllowed = null;
}
RolesAllowedRequestFilter(final String[] rolesAllowed) {
this.denyAll = false;
this.rolesAllowed = (rolesAllowed != null) ? rolesAllowed : new String[]{};
}
#Override
public void filter(final ContainerRequestContext requestContext) throws IOException {
if (!denyAll) {
if (rolesAllowed.length > 0 && !isAuthenticated(requestContext)) {
throw new ForbiddenException("Not Authorized");
}
for (final String role : rolesAllowed) {
if (requestContext.getSecurityContext().isUserInRole(role)) {
return;
}
}
}
throw new ForbiddenException("Not Authorized");
}
private static boolean isAuthenticated(final ContainerRequestContext requestContext) {
return requestContext.getSecurityContext().getUserPrincipal() != null;
}
}
}
First let me explain a bit about how the DynamicFeature works. For that let's first change the context of discussion to your current implementation of your AuthenticationFilter.
Right now it is a filter that is processed for every request. But let's say we introduced a custom #Authenticated annotation
#Target({METHOD, TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface Authenticated{}
We could use this annotation to annotate different methods and classes. To make it so that only the methods and classes annotated are filtered by the filter, we can introduce a DynamicFeature that checks for the annotation, then only register the filter when the annotation is found. For example
#Provider
public class AuthenticationDynamicFeature implements DynamicFeature {
#Override
public void configure(ResourceInfo resourceInfo, FeatureContext configuration) {
if (resourceInfo.getResourceMethod().isAnnotationPresent(Authenticated.class)) {
configuration.register(new AuthenticationFilter());
return;
}
if (resourceInfo.getResourceClass().isAnnotationPresent(Authenticated.class)) {
configuration.register(new AuthenticationFilter());
}
}
}
Once we register this AuthenticationDynamicFeature class, it will make it so that only methods and classes annotated with #Authenticated will be filtered.
Alternatively, this can even be done within the filter. We can get a reference to the ResourceInfo from within the AuthenticationFilter. For example check for the annotation, if is not there, then move on.
#Provider
#Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter {
#Context
private ResourceInfo resourceInfo;
#Override
public void filter(ContainerRequestContext context) throws IOException {
boolean hasAnnotation = false;
if (resourceInfo.getResourceMethod().isAnnotationPresent(Authenticated.class)
|| resourceInfo.getResourceClass().isAnnotationPresent(Authenticated.class)) {
hasAnnotation = true;
}
if (!hasAnnotation) return;
// process authentication is annotation is present
This way we could completely forget about the DynamicFeature. It's better to just use the DynamicFeature, I was just giving an example for demonstration purposes.
But that being said, if we look at the first block of code with the RolesAllowedDynamicFeature, you can better understand what is going on. It only registers the filter for methods and classes annotated with #RolesAllowed and #DenyAll. You could even refactor it to have all the annotation logic in the filter instead of the feature. You only have the filter. Just like I did with the AuthenticationFilter example above. Again this would be just for example purposes.
Now as far as the registration of the DynamicFeature, it works the same way as registering any other resource class or provider class (e.g. your authentication filter). So however you register those, just register the RolesAllowedDynamicFeature the same way. There is scanning, where #Path and #Provider annotations are scanned for. If this is what you are current using, then just annotating the feature class with #Provider should register it. For example just having an empty Application subclass will cause scanning to happen
#ApplicationPath("/api")
public class RestApplication extends Application {}
Then there is explicit registration in your Application subclass. For example
#ApplicationPath("/api")
public class RestApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<>();
classes.add(AuthenticationFilter.class);
classes.add(RolesAllowedFeature.class);
classes.add(SomeResource.class);
return classes;
}
}
Note that when doing this, you disable any scanning that goes on.
So a couple other things to make sure after all the above is clear it still isn't working.
Make sure your current AuthenticationFilter is annotated with #Priority(Priorities.AUTHENTICATION). This is to ensure that your authentication filter is called before the authorization filter. This needs to happen because the authentication filter is what sets the security context, and the authorization filter checks it.
Make sure you are creating the security context correctly. The authorization filter will call the SecurityContext.isUserInRole(role) passing in roles from the #RolesAllowed annotation. So you need to make sure to implements the isUserInRole correctly.
I would like to encapsulate Apache Shiro in a Servlet environment. I want to create MySecurityUtils and use Shiro SecurityUtils.getSubject in a static method. My question is whether this is a correct way to use SecurityUtils.getSubject method in a static method. Can this cause any problems in multithreaded servlet environment?
MySecurityUtils.java
import org.apache.shiro.subject.Subject;
import org.apache.shiro.SecurityUtils;
public class MySecurityUtils {
public static MyUser getUser() {
Subject currentUser = SecurityUtils.getSubject();
MyUser myUser = new MyUser(currentUser);
...
}
}
MyUser.java
public class MyUser {
// ... constructors
public boolean isPermitted(..) {subject.isPermitted(...)}
}
I don't see why you would want to do this, but for your question's sake, this would be fine.
In a web context, Shiro's SecurityUtils#getSubject() returns a different Subject instance per request. Obviously if the subject is logged in, the credentials will be copied over (from session) to the new Subject instance. You are pretty much doing the same thing by returning a new MyUser instance on each call to getUser().
Careful though, if you call getUser() twice in the same request, you will get a different MyUser instance. However, the internal Subject will be the same. It can be problematic if you are doing logic other than delegating in your MyUser class.
After feedback of Sotirios I changed my code as follows
public class SecurityHelper {
public static boolean isAuthenticated(){
Subject currentUser = SecurityUtils.getSubject();
return currentUser.isAuthenticated();
}
public static void checkPermission(String permissionCode){
Subject currentUser = SecurityUtils.getSubject();
currentUser.checkPermission(permissionCode);
}
public static void checkPermission(String... permissionCodes){
Subject currentUser = SecurityUtils.getSubject();
currentUser.checkPermissions(permissionCodes);
}
... and so on
I encapsulate all application logic in a Helper class.
I am using hiberbate 3.5 and spring 3.x
I have envers working and the ... _aud and revinfo record are now being written.
I now need to add the username to the audit record I'm guessing revinfo is the best place I have seen an example for seam app but nothing for spring app in jboss
Can anyone help with this please.
My main aim is to be able to record the windows authenticated user.
The Envers documentation has an answer for this if you use Seam.
public class ExampleListener implements RevisionListener {
public void newRevision(Object revisionEntity) {
ExampleRevEntity exampleRevEntity = (ExampleRevEntity) revisionEntity;
Identity identity = (Identity) Component.getInstance("org.jboss.seam.security.identity");
exampleRevEntity.setUsername(identity.getUsername());
}
}
So, I suppose your question would be how to retrieve the currently logged in user at that point if you don't use Seam? I'm sure there are several ways to do it, we do it like this.
Write a ServletFilter
In the filter get the principal from the session (or the security context if you use a security framework like Spring Security)
Store it in the log4j MDC
Code example from filter
protected void beforeRequest(final HttpServletRequest request, final String message) {
final Principal principal = request.getUserPrincipal();
final String username = principal != null ? principal.getName() : null;
if (username != null) {
MDC.put(USER, username);
}
}
protected void afterRequest(final HttpServletRequest request, final String message) {
MDC.remove(USER);
}
Later you can get the user from anywhere in your code because the MDC has a static get(String) method.
Short version: How do I get HttpServletRequest.getRemoteUser() to return the username when I am using a custom authentication filter?
Long version:
I am modifying a Tomcat application that currently uses declarative security (web.xml & tomcat-users.xml) to instead use a custom (written by me) authentication filter (derived from javax.servlet.Filter). There is a lot of information out there on how to do this and it looks very straightforward.
However, the existing application makes calls to HttpServletRequest.getRemoteUser(), and I assume that unless I do something to set this property in my filter, it will return null. I cannot find any information on how to populate the getRemoteUser() property in a filter (there is no setRemoteUser()). I found a post out there that recommends wrapping the request object in the filter. I will do this if I have to, but I am hoping there is a less invasive way to accomplish this.
Can anyone help?
Yes, the only way to modify an HttpServletRequest or HttpServletResponse is to decorate it and provide your own implementation for the methods of interest by overriding them. This is a standard pattern with authentication filters and that is the purpose of HttpServletRequestWrapper (the response counterpart is HttpServletResponseWrapper). We do it this way to wrap a kerberized request, as follows
public class KerbHttpServletRequest extends HttpServletRequestWrapper
{
private Principal myPrincipal;
private String myAuthType;
public KerbHttpServletRequest(HttpServletRequest aRequest,
Principal aPrincipal,
String aAuthType)
{
super(aRequest);
myPrincipal = aPrincipal;
myAuthType = aAuthType;
}
/**
* This method returns the Remote User name as user\#domain.com.
*/
#Override
public String getRemoteUser()
{
return myPrincipal.getName();
}
#Override
public String getAuthType()
{
return myAuthType;
}
#Override
public Principal getUserPrincipal()
{
return myPrincipal;
}
}