JSON Web Token (JWT) with HMAC protection - causing error with version 4.23 - nimbus

I was using nimbus-jose-jwt version 3.12 earlier and below code was working great. But when I updated nimbus-jose-jwt version 4.23 I see the following error coming
java.lang.Error: Unresolved compilation problems:
The constructor JWTClaimsSet() is undefined
The method setSubject(String) is undefined for the type JWTClaimsSet
The method setIssuer(String) is undefined for the type JWTClaimsSet
The method setExpirationTime(Date) is undefined for the type JWTClaimsSet
at springdemo.jwt.JWTWithHMACProtection.test(JWTWithHMACProtection.java:32)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
I am not sure what code should I need to modify, please guide
The code for reference:
#Test
public void test() throws KeyLengthException {
// Generate random 256-bit (32-byte) shared secret
SecureRandom random = new SecureRandom();
byte[] sharedSecret = new byte[32];
random.nextBytes(sharedSecret);
// Create HMAC signer
JWSSigner signer = new MACSigner(sharedSecret);
// Prepare JWT with claims set
JWTClaimsSet claimsSet = new JWTClaimsSet();
claimsSet.setSubject("alice");
claimsSet.setIssuer("https://c2id.com");
claimsSet.setExpirationTime(new Date(new Date().getTime() + 60 * 1000));
SignedJWT signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.HS256), claimsSet);
// Apply the HMAC protection
signedJWT.sign(signer);
// Serialize to compact form, produces something like
// eyJhbGciOiJIUzI1NiJ9.SGVsbG8sIHdvcmxkIQ.onO9Ihudz3WkiauDO2Uhyuz0Y18UASXlSc1eS0NkWyA
String s = signedJWT.serialize();
// On the consumer side, parse the JWS and verify its HMAC
signedJWT = SignedJWT.parse(s);
JWSVerifier verifier = new MACVerifier(sharedSecret);
Assert.assertTrue(signedJWT.verify(verifier));
// Retrieve / verify the JWT claims according to the app requirements
Assert.assertEquals("alice", signedJWT.getJWTClaimsSet().getSubject());
Assert.assertEquals("https://c2id.com", signedJWT.getJWTClaimsSet().getIssuer());
Assert.assertTrue(new Date().before(signedJWT.getJWTClaimsSet().getExpirationTime()));
}

I got the solution to this issue. Simply use the below code. Done! Its with the latest code
#Test
public void test() throws JOSEException, ParseException {
// Generate random 256-bit (32-byte) shared secret
SecureRandom random = new SecureRandom();
byte[] sharedSecret = new byte[32];
random.nextBytes(sharedSecret);
// Create HMAC signer
JWSSigner signer = new MACSigner(sharedSecret);
// Prepare JWT with claims set
JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
.subject("alice")
.issuer("https://c2id.com")
.expirationTime(addOneHour(new Date()))
.claim("http://example.com/is_root", true)
.build();
SignedJWT signedJWT = new SignedJWT(new JWSHeader(JWSAlgorithm.HS256), claimsSet);
// Apply the HMAC protection
signedJWT.sign(signer);
// Serialize to compact form, produces something like
// eyJhbGciOiJIUzI1NiJ9.SGVsbG8sIHdvcmxkIQ.onO9Ihudz3WkiauDO2Uhyuz0Y18UASXlSc1eS0NkWyA
String token = signedJWT.serialize();
System.out.println("Token : "+token);
// On the consumer side, parse the JWS and verify its HMAC
signedJWT = SignedJWT.parse(token);
JWSVerifier verifier = new MACVerifier(sharedSecret);
Assert.assertTrue(signedJWT.verify(verifier));
// Retrieve / verify the JWT claims according to the app requirements
Assert.assertEquals("alice", signedJWT.getJWTClaimsSet().getSubject());
Assert.assertEquals("https://c2id.com", signedJWT.getJWTClaimsSet().getIssuer());
Date date1 = new Date();
Date date2 = signedJWT.getJWTClaimsSet().getExpirationTime();
Assert.assertTrue(date1.compareTo(date2) > 0);
}
private Date addOneHour(Date currentDate){
DateTime dateTime = new DateTime(currentDate);
Date plusOnehour = dateTime.toDate();
return plusOnehour;
}

Related

C# DocuSign project getting "The type initializer for 'System.IdentityModel.Tokens.Jwt.JsonExtensions' threw an exception"

Looking for some help on why I am getting this runtime error when calling RequestJWTUserToken(). Here is a stack trace:
ERROR: The type initializer for 'System.IdentityModel.Tokens.Jwt.JsonExtensions' threw an exception.
at System.IdentityModel.Tokens.Jwt.JsonExtensions.SerializeToJson(Object value)
at System.IdentityModel.Tokens.Jwt.JwtHeader.SerializeToJson() in C:\agent1\_work\109\s\src\System.IdentityModel.Tokens.Jwt\JwtHeader.cs:line 319
at System.IdentityModel.Tokens.Jwt.JwtHeader.Base64UrlEncode() in C:\agent1\_work\109\s\src\System.IdentityModel.Tokens.Jwt\JwtHeader.cs:line 277
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.CreateJwtSecurityTokenPrivate(String issuer, String audience, ClaimsIdentity subject, Nullable`1 notBefore, Nullable`1 expires, Nullable`1 issuedAt, SigningCredentials signingCredentials, EncryptingCredentials encryptingCredentials) in C:\agent1\_work\109\s\src\System.IdentityModel.Tokens.Jwt\JwtSecurityTokenHandler.cs:line 506
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.CreateToken(SecurityTokenDescriptor tokenDescriptor) in C:\agent1\_work\109\s\src\System.IdentityModel.Tokens.Jwt\JwtSecurityTokenHandler.cs:line 468
at DocuSign.eSign.Client.ApiClient.RequestJWTUserToken(String clientId, String userId, String oauthBasePath, Byte[] privateKeyBytes, Int32 expiresInHours, List`1 scopes)
I have loaded the most-recent version of DocuSign.eSign in my project (5.8.0), and I have tried all the way back to 4.0.0 and get the same error.
Here is the source code (basically using the JWTAuth.cs code found in Quickstart). It's the RequestJWTUserToken(...) which causes the error:
var devPlaceholder = "https://demo.docusign.net/restapi"; // dev
ApiClient _apiClient = new ApiClient(devPlaceholder);
OAuth.OAuthToken authToken = _apiClient.RequestJWTUserToken(
"55xxxxxx-xxxx-xxxx-xxxxxxxxxxxxxx25dd",
"b5xxxxxx-xxxx-xxxx-xxxxxxxxxxxxxx26b4",
"account-d.docusign.com",
File.ReadAllBytes("C:\\secure\\pem_quickstart_secret_key.txt"),
1,
scopes);
string accessToken = authToken.access_token;
When I used Nuget to load DocuSign.eSign.dll (5.8.0) it also loaded dependencies:
BouncyCastle (1.8.9)
Microsoft.CSharp (4.5.0)
Microsoft.IdentityModel.JsonWebTokens (5.4.0)
Microsoft.IdentityModel.Logging (5.4.0)
Microsoft.IdentityModel.Protocols (5.4.0)
Microsoft.IdentityModel.Tokens (5.4.0)
Newtonsoft.Json (11.0.2)
RestSharp (106.12.0)
System.ComponentModel.Annotations (4.5.0)
System.IdentityModel.Tokens.Jwt (5.4.0)
I'm stumped on this one, as I don't know the inner workings of the Jwt assembly, and whether I am just missing some initialization of it or what is happening.
Below is an example of working API Call:
var integrationKey = "";
var impersonatedUserID = "";
var oauthBasePath = "account-d.docusign.com";
var privateKey = Encoding.UTF8.GetBytes(File.ReadAllText("private.key"));
var scopes = new List<string>
{
"signature",
"impersonation",
};
ApiClient _apiClient = new ApiClient();
OAuthToken _OAuthToken = _apiClient.RequestJWTUserToken(
integrationKey,
impersonatedUserID,
oauthBasePath,
privateKey, 1, scopes
);
accessToken = _OAuthToken.access_token;
NOTE: The private.key file should have the property Copy to output directory set to Always copy or Copy if newer.

Unable to connect to azure-blob-storage after upgrading from v8 to v12 java sdk in azure using spring boot

I am upgrading my existing spring boot application which uses azure-blob-storage from V8 SDK to V12 SDK. But I am getting authorization error. I almost tried all the examples suggested in azure sdk, but none of them are working. Below is the code between v8 and v12.
V8 (Working fine):
String endPoint = https://XXXXXXX.blob.core.windows.net/ecommerce/
String sasToken = sp=racwl&st=2021-06-01T05:12:04Z&se=2026-06-01T13:12:04Z&spr=https&sv=2020-02-05&sr=c&sig=XXXXXX%2BXXXXXXXXXXX%2BVVVVVVVVVVVV%3D
StorageCredentialsSharedAccessSignature s = new StorageCredentialsSharedAccessSignature("sas_token");
CloudBlobContainer cbc = new CloudBlobContainer(s.transformUri(new URI(endPoint)));
CloudBlobDirectory bd = cbc.getDirectoryReference("container_name");
InputStream is = new ByteArrayInputStream("my_string".getBytes());
CloudBlockBlob cbb = bd.getBlockBlobReference("blob_name");
cbb.upload(is, "my_string".length());
V12 (Failing with authentication):
String endPoint = https://XXXXXXX.blob.core.windows.net/ecommerce/
String sasToken = sp=racwl&st=2021-06-01T05:12:04Z&se=2026-06-01T13:12:04Z&spr=https&sv=2020-02-05&sr=c&sig=XXXXXX%2BXXXXXXXXXXX%2BVVVVVVVVVVVV%3D
BlobContainerClient bc = new BlobContainerClientBuilder().endpoint(endPoint).sasToken(sasToken).containerName("container_name").buildClient();
InputStream targetStream = new ByteArrayInputStream("my_string".getBytes());
BlockBlobClient cbb = bc.getBlobClient("blob_name").getBlockBlobClient();
cbb.upload(targetStream, payload.length()); ----> This is where it is throwing the exception
Error with V12 approach failing at authentication:
com.azure.storage.blob.models.BlobStorageException: If you are using a StorageSharedKeyCredential, and the server returned an error message that says 'Signature did not match', you can compare the string to sign with the one generated by the SDK. To log the string to sign, pass in the context key value pair 'Azure-Storage-Log-String-To-Sign': true to the appropriate method call.
If you are using a SAS token, and the server returned an error message that says 'Signature did not match', you can compare the string to sign with the one generated by the SDK. To log the string to sign, pass in the context key value pair 'Azure-Storage-Log-String-To-Sign': true to the appropriate generateSas method call.
Please remember to disable 'Azure-Storage-Log-String-To-Sign' before going to production as this string can potentially contain PII.
Status code 403, (empty body)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
...
I was able to connect successfully now with the below code. However, there is still an issue with the actuator/health page
BlobContainerClient bcc = new BlobContainerClientBuilder().endpoint(reportProperties.getEndpoint() + "/" + containerName + "/" + "?" + reportProperties.getSastoken()).buildClient();
BlobClient blobClient = bcc.getBlobClient(blobName);
InputStream is = new ByteArrayInputStream("my_string".getBytes());
blobClient.upload(is, payload.length());
After a days work this is how I managed to get this to work with SDK V12.14.1:
String endpoint = String.format(Locale.ROOT,
"https://%s.blob.core.windows.net", "myStorage");
AzureSasCredential sasCredential = new AzureSasCredential(
"sp=racwdl&st=2021-10-21T12:23:00Z&se=2021-10-21T20:23:00Z&spr=https&sv=2020-08-04&sr=c&sig=vV...MI%3D");
BlobServiceClient blobServiceClient = new BlobServiceClientBuilder()
.endpoint(endpoint).credential(sasCredential).buildClient();
BlobContainerClient blobContainerClient = blobServiceClient
.getBlobContainerClient("myContainer");
for (BlobItem blobItem : blobContainerClient.listBlobs()) {
BlockBlobClient blobClient = blobContainerClient
.getBlobClient(blobItem.getName()).getBlockBlobClient();
System.out.println(blobClient.getBlobName());
}

How to create KeyVaultClient instance using existing access_token string?

I have implemented Key Vault access token generator using below codebase:
private async Task<string> GetStaticToken(string authority, string resource)
{
var authContext = new AuthenticationContext(authority);
var credential = new ClientCredential(_appSettings.ClientId, _appSettings.ClientSecret);
AuthenticationResult result = await authContext.AcquireTokenAsync(resource, credential);
return result.AccessToken;
}
I know how to use this token into Authorization header and get the secret values using Rest API call. But can we use the same AccessToken string into below code base:
var builder = new ConfigurationBuilder();
var azureServiceTokenProvider = new AzureServiceTokenProvider();
var keyVaultClient = new KeyVaultClient(new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
builder.AddAzureKeyVault($"https://{myVaultName}.vault.azure.net/", keyVaultClient, new DefaultKeyVaultSecretManager());
Configuration = builder.Build();
Here is it possible to re-use AccessToken string value, while creating KeyVaultClient? Something like below:
var tokenValue = GetStaticToken (authority, resource);
var keyVaultClient = new KeyVaultClient(tokenValue);
Basically I would like to generate token at once and reuse string everywhere, even outside my application scope.
Note: I am aware that token will come with expiration time duration. That time GetToken will be called again.
Well, you can make a callback that returns that token:
var kvClient = new KeyVaultClient((authority, resource, scope) => Task.FromResult(tokenValue));
This simply replaces the call to get a token with an already completed Task with the token in it.

AdalException AADSTS90122: User identifier is not present

I get the following exception:
AADSTS90122: User identifier is not present
while calling:
authContext.AcquireTokenAsync(resourceId, clientCred, userAssertion)
full code:
ClientCredential clientCred = new ClientCredential(clientId, clientSecret);
var bootstrapContext = ClaimsPrincipal.Current.Identities.First().BootstrapContext as System.IdentityModel.Tokens.BootstrapContext;
string userName = ClaimsPrincipal.Current.FindFirst(ClaimTypes.Upn) != null ? ClaimsPrincipal.Current.FindFirst(ClaimTypes.Upn).Value : ClaimsPrincipal.Current.FindFirst(ClaimTypes.Email).Value;
string userAccessToken = bootstrapContext.Token;
UserAssertion userAssertion = new UserAssertion(userAccessToken, "urn:ietf:params:oauth:grant-type:jwt-bearer", userName);
string authority = aadInstance + tenantId;
AuthenticationContext authContext = new AuthenticationContext(authority, false, null);
var result = await authContext.AcquireTokenAsync(resourceId, clientCred, userAssertion);
100% verified parameters and right to have access.
The code was working for MONTHS, just yesterday started to hang :/
This issue with Azure Active directory seems to be fixed now. I'm able to acquire token on behalf of user successfully using UserAssertion and ClientCredentials now (from 13 Apr). This was not working for the past couple of days for sure.

Shiro complaining "There is no session with id xxx" with DefaultSecurityManager

I'm using Apache Shiro 1.2.0 in a long-running application that reads messages from a queue and and takes action. The action taken requires a Shiro authenticated session, so I've implemented an "ActAsAuthenticationToken" and custom credentials matcher which allows us to login in with only the username. I'm using the DefaultSecurityManager with only my custom realm and subject factory injected. Everything else should be default.
As it is configured, everything worked fine for a while, but as the application ran a long time (not that long - like a full day) I started to get this stack trace whenever I did anything that required the session:
Caused by: org.apache.shiro.session.UnknownSessionException: There is no session with id [f5b7c3bf-2c53-40e9-a707-37f4265970aa]
at org.apache.shiro.session.mgt.eis.AbstractSessionDAO.readSession(AbstractSessionDAO.java:170)
at org.apache.shiro.session.mgt.DefaultSessionManager.retrieveSessionFromDataSource(DefaultSessionManager.java:236)
at org.apache.shiro.session.mgt.DefaultSessionManager.retrieveSession(DefaultSessionManager.java:222)
at org.apache.shiro.session.mgt.AbstractValidatingSessionManager.doGetSession(AbstractValidatingSessionManager.java:118)
at org.apache.shiro.session.mgt.AbstractNativeSessionManager.lookupSession(AbstractNativeSessionManager.java:105)
at org.apache.shiro.session.mgt.AbstractNativeSessionManager.lookupRequiredSession(AbstractNativeSessionManager.java:109)
at org.apache.shiro.session.mgt.AbstractNativeSessionManager.getAttribute(AbstractNativeSessionManager.java:206)
at org.apache.shiro.session.mgt.DelegatingSession.getAttribute(DelegatingSession.java:141)
at org.apache.shiro.session.ProxiedSession.getAttribute(ProxiedSession.java:121)
at org.apache.shiro.session.ProxiedSession.getAttribute(ProxiedSession.java:121)
at org.apache.shiro.session.ProxiedSession.getAttribute(ProxiedSession.java:121)
at com.factorlab.security.FactorlabDelegatingSubject.getUser(FactorlabDelegatingSubject.java:34)
at com.factorlab.security.FactorlabDelegatingSubject.getUser(FactorlabDelegatingSubject.java:10)
at com.factorlab.persistence.AbstractEntityDao.getCurrentUser(AbstractEntityDao.java:227)
at com.factorlab.persistence.AbstractEntityDao.fireEvent(AbstractEntityDao.java:215)
at com.factorlab.persistence.AbstractEntityDao.saveOrUpdate(AbstractEntityDao.java:190)
at com.factorlab.persistence.AbstractEntityDao.saveOrUpdate(AbstractEntityDao.java:177)
at com.factorlab.persistence.AbstractEntityDao.saveOrUpdate(AbstractEntityDao.java:38)
at sun.reflect.GeneratedMethodAccessor106.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:616)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:318)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:196)
at $Proxy72.saveOrUpdate(Unknown Source)
at com.factorlab.observations.sales.OpportunityScoreUpdateServiceImpl.receiveOpportunityEvent(OpportunityScoreUpdateServiceImpl.java:83)
at sun.reflect.GeneratedMethodAccessor103.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:616)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:318)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
at $Proxy76.receiveOpportunityEvent(Unknown Source)
at sun.reflect.GeneratedMethodAccessor102.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:616)
at org.springframework.expression.spel.support.ReflectiveMethodExecutor.execute(ReflectiveMethodExecutor.java:69)
at org.springframework.expression.spel.ast.MethodReference.getValueInternal(MethodReference.java:84)
at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:57)
at org.springframework.expression.spel.ast.SpelNodeImpl.getTypedValue(SpelNodeImpl.java:102)
at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:102)
at org.springframework.integration.util.AbstractExpressionEvaluator.evaluateExpression(AbstractExpressionEvaluator.java:126)
at org.springframework.integration.util.MessagingMethodInvokerHelper.processInternal(MessagingMethodInvokerHelper.java:227)
at org.springframework.integration.util.MessagingMethodInvokerHelper.process(MessagingMethodInvokerHelper.java:127)
at org.springframework.integration.handler.MethodInvokingMessageProcessor.processMessage(MethodInvokingMessageProcessor.java:73)
... 49 more
The really weird part (as far as I'm concerned) is that I have a successful login (or at least an indication that I already have been authenticated right before I get the error:
#Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.REPEATABLE_READ)
#Trace(dispatcher = true)
public void receiveOpportunityEvent(EntityEvent<Opportunity> event) {
sessionFactory.getCurrentSession().refresh(event.getEntity());
log.info("OpportunityScoreUpdateService receiveOpportunityEvent: " + event);
//
//
// Here we see that we are either authenticated or we log in successfully
//
//
if (!securityUtils.getSubject().isAuthenticated()) {
try {
securityUtils.getFactorlabSubject().login(new ActAsAuthenticationToken(event.getEventUsername()));
} catch (RuntimeException e) {
log.error("Could not log in user " + event.getEventUsername() + ": " + e.getMessage(), e);
return;
}
}
if (event.getEntity() instanceof ObservedOpportunity) {
ObservedOpportunity opportunity = (ObservedOpportunity) event.getEntity();
opportunity = (ObservedOpportunity) opportunityDao.getById(opportunity.getId(), SkippedCheck.PERMISSION, SkippedCheck.DELETED);
if (!opportunity.isDeleted()) {
List<Stage> stages = stageDao.getAllByZone(opportunity.getZone(), SkippedCheck.PERMISSION);
Map<Stage, Double> originalScoresByStage = new HashMap<Stage, Double>();
Map<Stage, Double> newScoresByStage = new HashMap<Stage, Double>();
final Double originalTotal = opportunity.getTotalScore();
for (Stage stage : stages) {
originalScoresByStage.put(stage, opportunity.getScoreByStage(stage));
double score = calculator.getScoreForOpportunityAndStage(opportunity, stage);
opportunity.setScoreByStage(stage, score);
newScoresByStage.put(stage, opportunity.getScoreByStage(stage));
}
final double newTotalScore = calculator.getTotalScoreForOpportunity(opportunity);
opportunity.setTheTotalScore(newTotalScore);
final boolean scoreChanged = originalTotal == null ||
Math.round(originalTotal) != Math.round(newTotalScore) ||
checkStageScoresChanged(originalScoresByStage, newScoresByStage);
if (scoreChanged) {
opportunity.setScoreCalculated(new Date());
//
//
// Here is where we get the exception
//
//
opportunityDao.saveOrUpdate(opportunity, SkippedCheck.PERMISSION);
} else {
opportunityDao.refresh(opportunity);
}
}
}
}
What could be causing this exception?
I was getting this error and found that completely destroying any existing session before calling subject.login(credentials) fixed it.
// Login the user
private Subject loginUser()
{
ensureUserIsLoggedOut();
Subject subject = SecurityUtils.getSubject();
subject.login(credentials);
}
And the supporting routines are:
// Logout the user fully before continuing.
private void ensureUserIsLoggedOut()
{
try
{
// Get the user if one is logged in.
Subject currentUser = SecurityUtils.getSubject();
if (currentUser == null)
return;
// Log the user out and kill their session if possible.
currentUser.logout();
Session session = currentUser.getSession(false);
if (session == null)
return;
session.stop();
}
catch (Exception e)
{
// Ignore all errors, as we're trying to silently
// log the user out.
}
}
Shiro is validating credentials against SecuritySubject, which is stored in Session. So, it's very likely your session expired after some time of inactivity. You can change expiration time in web.xml or you can use Shiro rememberMe function, but your client have to support cookies. After rememberMe function SecuritySubject will obtain different session and will return false against isAuthenticated, but isRemembered will return true.
The session will never expired This will produce another problem, when your session will never expire. It will most likely get you out of memory, because your web container is most likely using memory session manager.
<session-config>
<session-timeout>-1</session-timeout>
</session-config>
Shiro rememberMe
http://shiro.apache.org/java-authentication-guide.html
//Example using most common scenario:
//String username and password. Acquire in
//system-specific manner (HTTP request, GUI, etc)
UsernamePasswordToken token =
new UsernamePasswordToken( username, password );
//”Remember Me” built-in, just do this:
token.setRememberMe(true);
We can disable the session storage in shiro.
The org.apache.shiro.mgt.DefaultSessionStorageEvaluator class contains a flag called sessionStorageEnabled. We can make it false.
I use the following in my spring application context for not using session storage.
<bean id="defaultSessionStorageEvaluator" class="org.apache.shiro.mgt.DefaultSessionStorageEvaluator">
<property name="sessionStorageEnabled" value="false" />
<bean id="defaultSubjectDAO" class="org.apache.shiro.mgt.DefaultSubjectDAO">
<property name="sessionStorageEvaluator" ref="defaultSessionStorageEvaluator" />
</bean>

Resources