Study a long time Springcloud micro-service architecture, here to summarize, do a comb and memo.
This is a summary of the certification between micro-services. A new set of self-authentication frameworks for apps and browsers for single and distributed applications based on spring security has recently been implemented. There is a bit more in-depth understanding of spring security, and here's a OAUTH2+JWT to solve the problem of authentication between microservices.
The details are not covered here, and there is a lot of content about spring security and OAUTH2,JWT. In particular, to use the spring security, read its source code is essential. Here is a few notes in the learning process, remember the more chaotic.
Some notes of spring security
Some of the notes from spring OAuth
In addition, note that there is no but very important and basic content, spring security through the filter chain to achieve authentication. Verification code verification by comparing the values in Redis, the validation logic needs to extend a filter and put it before usernamepasswordauthenticationfilter. SMS Verification code because the phone number is provided, so need to userdetailsservice to determine whether the mobile phone number exists, so need to provide a authenticationprovider call Userdetailsservice to do the verification. The verification of the mobile phone information needs to pass a filter package, the filter should be placed after the usernamepasswordauthenticationfilter, to ensure that the final authentication class object is based on the mobile phone number of the test.
In fact, spring security itself provides an SSO function, also use OAUTH2, can also use JWT, but it is very awkward, it can not implement its own resource server in the microservices. After I provided my own resource server in the microservices, I couldn't get token. If you do not have your own resource server, it does not work to use tokens from the authentication server after the front-end separation. This is true for browsers where there is no separate application for the front and back ends. But for apps and front-end separation applications It's a hassle. So here's how to do it. OAUTH2+JWT Single Sign-on for app, front-end separation.
Finally, there is a problem where tokens in the request header are lost if the feign is used to make a call between microservices. This needs to be read feign source code, the corresponding extension, before calling HttpClient, the information in the header is added to the invoked MicroServices HTTP request.
First, the idea:
We need a unified authentication server for authentication, including graphics verification Code and SMS Verification code, to obtain the token which the Micro service recognizes.
Due to the separation of the front and back, the resource server must be implemented on the micro-service, in order to ensure the identification of tokens, traditional practices in the resource server authentication can not be done. Then you need to configure the authentication server with the browser's authorization. The graphics verification code and the SMS verification code must also be extended in the browser authentication. So the idea is to first through the browser of the user name password, graphics verification code or SMS Verification code authentication, and then through the authentication server with OAuth2 password mode to obtain the token based on JWT. Implement a resource server to parse tokens on a microservices service.
In this way, authentication and token issuance must be implemented separately.
For the feign method to invoke the token in the missing request header between microservices, it is necessary to implement the Requestinterceptor interceptor, in which the token information is added to the request header of the called MicroServices.
1, authentication server + browser authentication
Provides an authentication server that is used to issue JWT-based tokens. Provide a browser-based authentication configuration to customize your own authentication logic. To expand Authentication, provide the authentication code of the graphics and SMS Verification code. The specific way of expansion will not say, if so, it is unavoidable to analyze the source of spring security, which is enough to write another article.
Here to say the result of the extension, through the Authentication/form user name password plus the authentication code, need to provide DeviceID in the request header. The authentication code of the mobile phone is authenticated by Authentication/mobile, and the DeviceID must be provided in the request header. Then, after the authentication is passed, the token is obtained through Oauth/token, and a basic-based request header verification is provided in the request with a value of Clientid:clientsecret base64 encoding.
There is a problem here, the verification code of the mobile phone verification extension is not required to provide a password, but we get token OAuth2 mode for the password mode, need to provide a password. How to solve this contradiction. My approach is as follows:
A, in the implementation of the Authenticationprovider interface provides a sign to indicate the verification of SMS. Each time the Userdetailsservice.loaduserbyusername method is called in the class, the user name is added to the flag.
In this way, the Loaduserbyusername method called by Usernamepasswordauthenticationfilter will not be changed to determine whether it is from SMS verification.
b, if it is the verification of SMS verification code, you need to add such a key value pair in Redis, the key for mobile phone verification code sign plus mobile phone number, the value of the mobile phone verification code. Note that the expiration time cannot be too long.
C, OAuth2 password mode verification, password for mobile phone verification code, through the Redis can determine whether the authentication from the mobile phone verification code, if it comes from the mobile phone verification code, the use of mobile phone number and phone verification code as user return.
This implementation, or the code.
/** * * Userdetailsservice the Loaduserbyusername method to determine if there is a user * to enclose the result in token, where token is the base class authentication class , not the token of JWT * * @author Wulinfeng * * */public class Smscodeauthenticationprovider implements Authenticationprovider
{public static final String Sms_flag = "20447a87-4947-11e8-95a3-a087c5481799";
Private Userdetailsservice Userdetailsservice; @Override Public authentication Authenticate (authentication authentication) throws Authenticationexception {UserDetai
ls userdetails = userdetailsservice.loaduserbyusername (sms_flag+ ":" + (String) Authentication.getprincipal ())); Optional.ofnullable (userdetails). Orelsethrow (()->{return new Internalauthenticationserviceexception ("Unable to get user Interest.
");
}); Smscodeauthenticationtoken Smscodeauthenticationtoken = new Smscodeauthenticationtoken (UserDetails.getUsername (),
Userdetails.getauthorities ());
Smscodeauthenticationtoken.setdetails (Authentication.getdetails ()); Return SmscodeauthenticationtokeN } @Override public Boolean supports (class<?> authentication) {return (SmsCodeAuthenticationToken.class.isAssig
Nablefrom (authentication));
} public Userdetailsservice Getuserdetailsservice () {return userdetailsservice; } public void Setuserdetailsservice (Userdetailsservice userdetailsservice) {this.userdetailsservice = UserDetailsServ
Ice }
}
@Component public class Ssouserdetailsservice implements Userdetailsservice {@Autowired private passwordencoder Passwo
Rdencoder;
@Autowired private redistemplate<string, string> redistemplate;
@Override public userdetails loaduserbyusername (String username) throws Usernamenotfoundexception {User user = null;
string[] usersegment = Username.split (":"); if (Usersegment.length > 1) {//SMS authentication if (SmsCodeAuthenticationProvider.SMS_FLAG.equals (Usersegment[0])) {STR
ing phone = usersegment[2];
String SMS = usersegment[1];
user = Loaduserbysms (phone, SMS);
} else {user = Loaduser (username);
}} else {user = Loaduser (username);
} return user;
Private user Loaduser (String username) {User user = null;
String SMS = Redistemplate.opsforvalue (). Get (smscodeauthenticationprovider.sms_flag+ ":" +username); if (!
Stringutils.isempty (SMS)) {Redistemplate.delete (smscodeauthenticationprovider.sms_flag+ ":" +username); user = New User (username, Passwordencoder.encode (SMS), Authorityutils.commaseparatedstringtoauthoritylist ("Role_user")); } else {//To query user information based on user name or phone number users = New User (username, passwordencoder.encode ("123456"), Authorityutils.commasepara
Tedstringtoauthoritylist ("Role_user"));
} return user; } Private User Loaduserbysms (string phone, string sms) {//query user information based on phone number redistemplate.opsforvalue (). Set (smscodeauthe
nticationprovider.sms_flag+ ":" +phone, SMS, timeunit.seconds); return new User (phone, Passwordencoder.encode (phone), authorityutils.commaseparatedstringtoauthoritylist ("Role_
USER ")); }
}
2. Micro-Service Implements resource server
MicroServices only need to implement the most basic resource server, is mainly used to verify and parse tokens. Note that the check here is only verified by the secret key and determines if token is out of date.
3, micro-service between feign call request header loss problem
Implements the Requestinterceptor interceptor, in which token information in the request header is added to the request header of the called MicroServices
public class Ssofeignconfig implements Requestinterceptor {public static String Token_header = "Autho
Rization "; @Override public void Apply (requesttemplate template) {Template.header (Token_header, Getheaders (
Gethttpservletrequest ()). Get (Token_header)); } private HttpServletRequest Gethttpservletrequest () {try {return (servletrequestattributes) Requ
Estcontextholder.getrequestattributes ()). Getrequest ();
} catch (Exception e) {return null; }} private Map<string, string> getheaders (HttpServletRequest request) {map<string, string> ma
p = new linkedhashmap<> ();
Enumeration<string> enumeration = Request.getheadernames ();
while (Enumeration.hasmoreelements ()) {String key = Enumeration.nextelement ();
String value = Request.getheader (key);
Map.put (key, value);
} return map; }
}
@FeignClient (name= "Client1-server/client1", fallback=userserviceimpl.class, configuration = ssofeignconfig.class) Public
interface UserService {
@GetMapping (value= "/user")
Authentication User (authentication user);
@GetMapping (value= "/test")
String Test ();
This is an implementation of micro-service based on OAUTH2+JWT Single sign-on. The specifics of the implementation are very, very much content. Here is not to say, only to provide a general idea of the memo.