问题
Preamble
I'm designing an API gateway for a Grails microservice federation. This issue seems related to a bunch of issues already filed in this repository but nothing provides a solution.
Versions and Configurations
Grails: 3.2.2
Tomcat: 8.5
Plugin versions:
compile 'org.grails.plugins:spring-security-core:3.1.2'
compile "org.grails.plugins:spring-security-rest:2.0.0.M2"
I'm using spring security rest plugin for only token authentication. I'm doing the authorization part myself by returning ROLE_NO_ROLES
for all the users in getAuthorities()
. I intercept all the requests and authorize the access based on my own authorization schema stored in DB.
Problem:
With these configurations and strategy, my code works as desired when I run it on my local system. When I deploy it on a server as a war file in tomcat, it works fine for all the requests to the gateway, i.e., for all requests of the pattern /umm/controller/action
. Spring security context is there and the user is evaluated perfectly.
When I try to call other microservices by redirection with requests of the form /umm/microservice/controller/action
, springSecurityService.getCurrentUser()
and springSecurityService?.principal?.username
start to return null. Although my token gets evaluated perfectly, yet I'm not getting any security context.
For details, have a look on this issue. The details for reproducing the bug are also provided in the aforementioned issue. The whole project is available here.
Update: May 19, 2017
I tried deploying my war in a Tomcat on my local machine. This question and this question provide following solutions.
- disabling tomcat cache
- setting
grails.plugin.springsecurity.sch.strategyName = org.springframework.security.core.context.SecurityContextHolder.MODE_INHERITABLETHREADLOCAL
Nothing seems to work so far. SecurityContextHolder
is returning null
anyway. All the user retrieving functions of SpringSecurityService
viz. getCurrentUser()
, getPrincipal()
, getAuthentication()
and loadCurrentUser()
return null.
Update: May 23, 2017
To narrow down the problem, I executed the standalone war using
java -Dgrails.env=prod -jar build/libs/mywar-0.1.war
Now for any non-umm request, I get a 404, page not found
. I think the problem is with the production environment. The app works completely fine in the development.
Also tried grails run-app
which works fine. To rule out the problem with the production environment, I created the war using grails dev war
but to no avail. Nothing works so far for the war
.
Update: May 25, 2017
I should probably ask this http://security.stackexchange.com but for the record, I'm asking it here too.
The answer provided by me below contains a workaround fix. The mechanism by which the fix works is explained in the answer. My question is:
- Does this approach introduce any vulnerability or loophole in the security system?
- Is this authorization schema safe or needs to be revised?
- I'm authenticating through the plugin but authorizing myself. Can somebody bypass the security filters and hit the authorization interceptor directly? Because if someone can do that, he will only have to give me an admin's username in the same format as the token does and he'll have access to everything.
回答1:
Workaround
I got a workaround fix to the problem. Since I needed only the username, I grabbed the rest token in my interceptor, decoded it and extracted the username out of it.
Here goes:
def extractUsername(def token){
Base64 coder = new Base64()
def tok = token - "Bearer "
def principal = tok.tokenize(".")
def dec = coder.decode(principal[1])
def sub = new String(dec)
def user = sub.tokenize(",")
def username=user[1].tokenize(":")
username = username[1]-"\""
return username-"\""
}
It worked for me because I didn't need to check the springSecurityService.Principal
object. Had it been the case, I wouldn't have been able to get the username. springSecurityService.Principal
and springSecurityService.getCurrentUser()
are still returning null. The issue is not resolved yet. I'm answering because I didn't get even a single comment despite having a bounty. Answer to the original question is still welcome if anybody can explain why spring-security-plugin is behaving this way.
Edit: May 25, 2017
The workaround I used is based on the structure of the token and the fact that username is embedded in the token and token is simply base64 encoded.
This is the original token generated by spring security REST plugin for grails:
eyJhbGciOiJIUzI1NiJ9.eyJwcmluY2lwYWwiOiJINHNJQUFBQUFBQUFBSlZTUDBcL2JRQlJcL0RrRkJpbWdCcVpVNjBLVjBxeHlKanBsSVNwRXFLNmthc2xDcDZHSVwvM0lQem5iazdrMlNwTXBXQkFVU0xWSld2d0RlQmhROVF0VU5YNXE1OVp3aE91eUJ1c2ZYdTU5K1wvNTdNcm1EWWFYc2FhY1dIOFZHUXhsNzVKTlpleHdURFQzQTc5ektDTzBPYUl0UnpZcFFsY0g2OEVYZ0FsSGxsWUNMYlpIcXNKSnVOYXU3ZU5vYTBQTkN3ckhkOHdibW1XWUZcL3BIZitXTzFRYVwveEVvcUwzdkphaHN3RHdMUTVWSjIxSnlkWkJ5amRFR3pCV3pRSVU3YnZRb3BCdVVsak5oSnFFVmxLd25NQXFneWpMN1VaRXFSMlBoNGJYWnpISlI2NkN0QnpDVE1tUEkzWDlKT3RaWmRcL2ZPcHFRRXVcL0FKeW9QVW8wUGRQWGRRM1wvSDRUU1VFcGVaS21xV3VURlRFdDdnVEpcLzdSNHZIbDRlbW9Xd0tnVGw3Y1wvVTB4ZjlLQTBmbUhQMFwvem9yM1F3dU1KNndXc1Brakp6WHpCdks3UktmXC80OXZiTHlkWCsreWxTZG9qWDk5XC9IMHNwTmM4T21TbEttbVZVVE95TGFmdG05RTNuamJ2THhGb1oraHllcFFQcWpwTVhvVnFJZ3ByaGxyY1M0Ynd1ejc5ckI2bWFydmVtZUhUZXBzQ2poOGxXRHBCXC9reWQzS1wvRURSd2c1K0gxMGNQdnRKTkc5Z2VvK0pES240dVFMVXlwSWU2czluSjR2VnI3OE84aGpqWFwvb3Z4RG9UU3hZREFBQT0iLCJzdWIiOiJhZG1pbkRCIiwicm9sZXMiOlsiUk9MRV9OT19ST0xFUyJdLCJleHAiOjE0OTU3MTU3OTMsImlhdCI6MTQ5NTcxMjE5M30.MPEXURGhJo5s75LfUSm5ckG99Byc7FCLyj1gYZJu1zk
This is decoded version:
"principal":"H4sIAAAAAAAAAJVSP0/bQBR/DkFBimgBqZU60KV0qxyJjplISpEqK6kaslCp6GI/3IPznbk7k2SpMpWBAUSLVJWvwDeBhQ9QtUNX5q59ZwhOuyBusfXu59+/57MrmDYaXsaacWH8VGQxl75JNZexwTDT3A79zKCO0OaItRzYpQlcH68EXgAlHllYCLbZHqsJJuNau7eNoa0PNCwrHd8wbmmWYF/pHf+WO1Qa/xEoqL3vJahswDwLQ5VJ21JydZByjdEGzBWzQIU7bvQopBuUljNhJqEVlKwnMAqgyjL7UZEqR2Ph4bXZzHJR66CtBzCTMmPI3X9JOtZZd/fOpqQEu/AJyoPUo0PdPXdQ3/H4TSUEpeZKmqWuTFTEt7gTJ/7R4vHl4emoWwKgTl7c/U0xf9KA0fmHP0/zor3QwuMJ6wWsPkjJzXzBvK7RKf/49vbLydX++ylSdojX99/H0spNc8OmSlKmmVUTOyLaftm9E3njbvLxFoZ+hyepQPqjpMXoVqIgprhlrcS4bwuz79rB6marvemeHTepsCjh8lWDpB/kyd3K/EDRwg5+H10cPvtJNG9geo+JDKn4uQLUypIe6s9nJ4vVr78O8hjjX/ovxDoTSxYDAAA=","sub":"adminDB","roles":["ROLE_NO_ROLES"],"exp":1495715793,"iat":1495712193
The principal here is signed and encrypted but the username is not encrypted. So it can be easily extracted by the code given above.
来源:https://stackoverflow.com/questions/43999961/springsecurityservice-principal-returns-null-when-deployed-as-a-war-in-tomcat-8