在分布式环境中,如何支持PC、APP(ios、android)等多端的会话共享,这也是所有公司都需要的解决方案,用传统的session方式来解决,我想已经out了,我们是否可以找一个通用的方案,比如用传统cas来实现多系统之间的sso单点登录或使用oauth的第三方登录方案? 今天给大家简单讲解一下使用spring拦截器Interceptor机制、jwt认证方式、redis分布式缓存实现sso单点登录,闲话少说,直接把步骤记录下来分享给大家:
1. 引入jwt的相关jar包,在项目pom.xml中引入:
-
<dependency>
-
<groupId>com.auth0</groupId>
-
<artifactId>java-jwt</artifactId>
-
<version>
2.2
.
0
</version>
-
</dependency>
2. 拦截器配置:
-
<mvc:interceptor>
-
<mvc:mapping path=
"${adminPath}/**"
/>
-
<mvc:exclude-mapping path=
"${adminPath}/rest/login"
/>
-
<bean
class
=
"com.ml.honghu.interceptor.LoginInterceptor"
/>
-
</mvc:interceptor>
我这里简单配置了要拦截的url和过滤的url(这个根据自己项目来定)
3. 编写jwt的加密或者解密工具类:
-
public
class
JWT {
-
private
static
final
String SECRET =
"HONGHUJWT1234567890QWERTYUIOPASDFGHJKLZXCVBNM"
;
-
-
private
static
final
String EXP =
"exp"
;
-
-
private
static
final
String PAYLOAD =
"payload"
;
-
-
-
public
static
<T> String sign(T object,
long
maxAge) {
-
try
{
-
final
JWTSigner signer =
new
JWTSigner(SECRET);
-
final
Map<String, Object> claims =
new
HashMap<String, Object>();
-
ObjectMapper mapper =
new
ObjectMapper();
-
String jsonString = mapper.writeValueAsString(object);
-
claims.put(PAYLOAD, jsonString);
-
claims.put(EXP, System.currentTimeMillis() + maxAge);
-
return
signer.sign(claims);
-
}
catch
(Exception e) {
-
return
null
;
-
}
-
}
-
-
-
public
static
<T> T unsign(String jwt, Class<T> classT) {
-
final
JWTVerifier verifier =
new
JWTVerifier(SECRET);
-
try
{
-
final
Map<String,Object> claims= verifier.verify(jwt);
-
if
(claims.containsKey(EXP) && claims.containsKey(PAYLOAD)) {
-
String json = (String)claims.get(PAYLOAD);
-
ObjectMapper objectMapper =
new
ObjectMapper();
-
return
objectMapper.readValue(json, classT);
-
-
}
-
return
null
;
-
}
catch
(Exception e) {
-
return
null
;
-
}
-
}
-
}
这个加密工具类是我从网上找的,如果各位要修改,可以按照自己业务修改即可。
4. 创建Login.java对象,用来进行jwt的加密或者解密:
-
public
class
Login
implements
Serializable{
-
-
-
-
private
static
final
long
serialVersionUID = 1899232511233819216L;
-
-
-
-
-
private
String uid;
-
-
-
-
-
private
String loginName;
-
-
-
-
-
private
String password;
-
-
public
Login(){
-
super
();
-
}
-
-
public
Login(String uid, String loginName, String password){
-
this
.uid = uid;
-
this
.loginName = loginName;
-
this
.password = password;
-
}
-
-
public
String getUid() {
-
return
uid;
-
}
-
public
void
setUid(String uid) {
-
this
.uid = uid;
-
}
-
public
String getLoginName() {
-
return
loginName;
-
}
-
public
void
setLoginName(String loginName) {
-
this
.loginName = loginName;
-
}
-
public
String getPassword() {
-
return
password;
-
}
-
public
void
setPassword(String password) {
-
this
.password = password;
-
}
-
-
-
}
5. 定义RedisLogin对象,用来通过uid往redis进行user对象存储:
-
public
class
RedisLogin
implements
Serializable{
-
-
-
-
private
static
final
long
serialVersionUID = 8116817810829835862L;
-
-
-
-
-
private
String uid;
-
-
-
-
-
private
String token;
-
-
-
-
-
private
long
refTime;
-
-
public
RedisLogin(){
-
-
}
-
-
public
RedisLogin(String uid, String token,
long
refTime){
-
this
.uid = uid;
-
this
.token = token;
-
this
.refTime = refTime;
-
}
-
-
public
String getUid() {
-
return
uid;
-
}
-
public
void
setUid(String uid) {
-
this
.uid = uid;
-
}
-
public
String getToken() {
-
return
token;
-
}
-
public
void
setToken(String token) {
-
this
.token = token;
-
}
-
public
long
getRefTime() {
-
return
refTime;
-
}
-
public
void
setRefTime(
long
refTime) {
-
this
.refTime = refTime;
-
}
-
-
-
-
}
6. 编写LoginInterceptor.java拦截器
-
public
class
LoginInterceptor
implements
HandlerInterceptor{
-
-
public
boolean
preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
-
throws
Exception {
-
PrintWriter writer =
null
;
-
HandlerMethod method =
null
;
-
try
{
-
method = (HandlerMethod) handler;
-
}
catch
(Exception e) {
-
writer = response.getWriter();
-
ResponseVO responseVO = ResponseCode.buildEnumResponseVO(ResponseCode.REQUEST_URL_NOT_SERVICE,
false
);
-
responseMessage(response, writer, responseVO);
-
return
false
;
-
}
-
IsLogin isLogin = method.getMethodAnnotation(IsLogin.
class
);
-
if
(
null
== isLogin){
-
return
true
;
-
}
-
-
-
response.setCharacterEncoding(
"utf-8"
);
-
String token = request.getHeader(
"token"
);
-
String uid = request.getHeader(
"uid"
);
-
-
if
(StringUtils.isEmpty(token)) {
-
writer = response.getWriter();
-
ResponseVO responseVO = LoginResponseCode.buildEnumResponseVO(LoginResponseCode.LOGIN_TOKEN_NOT_NULL,
false
);
-
responseMessage(response, writer, responseVO);
-
return
false
;
-
}
-
if
(StringUtils.isEmpty(uid)){
-
writer = response.getWriter();
-
ResponseVO responseVO = LoginResponseCode.buildEnumResponseVO(LoginResponseCode.USERID_NOT_NULL,
false
);
-
responseMessage(response, writer, responseVO);
-
return
false
;
-
}
-
-
Login login = JWT.unsign(token, Login.
class
);
-
-
if
(
null
== login || !StringUtils.equals(login.getUid(), uid)){
-
writer = response.getWriter();
-
ResponseVO responseVO = LoginResponseCode.buildEnumResponseVO(LoginResponseCode.USERID_NOT_UNAUTHORIZED,
false
);
-
responseMessage(response, writer, responseVO);
-
return
false
;
-
}
-
-
-
RedisLogin redisLogin = (RedisLogin)JedisUtils.getObject(uid);
-
if
(
null
== redisLogin){
-
writer = response.getWriter();
-
ResponseVO responseVO = LoginResponseCode.buildEnumResponseVO(LoginResponseCode.RESPONSE_CODE_UNLOGIN_ERROR,
false
);
-
responseMessage(response, writer, responseVO);
-
return
false
;
-
}
-
-
if
(!StringUtils.equals(token, redisLogin.getToken())){
-
writer = response.getWriter();
-
ResponseVO responseVO = LoginResponseCode.buildEnumResponseVO(LoginResponseCode.USERID_NOT_UNAUTHORIZED,
false
);
-
responseMessage(response, writer, responseVO);
-
return
false
;
-
}
-
-
if
(System.currentTimeMillis() > redisLogin.getRefTime()) {
-
writer = response.getWriter();
-
ResponseVO responseVO = LoginResponseCode.buildEnumResponseVO(LoginResponseCode.LOGIN_TIME_EXP,
false
);
-
responseMessage(response, writer, responseVO);
-
return
false
;
-
}
-
-
-
redisLogin =
new
RedisLogin(uid, token, System.currentTimeMillis() + 60L* 1000L* 30L);
-
JedisUtils.setObject(uid , redisLogin,
360000000
);
-
return
true
;
-
}
-
-
public
void
postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
-
ModelAndView modelAndView)
throws
Exception {
-
-
}
-
-
public
void
afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
-
throws
Exception {
-
-
}
-
-
private
void
responseMessage(HttpServletResponse response, PrintWriter out, ResponseVO responseVO) {
-
response.setContentType(
"application/json; charset=utf-8"
);
-
JSONObject result =
new
JSONObject();
-
result.put(
"result"
, responseVO);
-
out.print(result);
-
out.flush();
-
out.close();
-
}
-
-
}
7. 定义异常的LoginResponseCode
-
public
enum
LoginResponseCode {
-
USERID_NOT_NULL(
3001
,
"用户id不能为空."
),
-
LOGIN_TOKEN_NOT_NULL(
3002
,
"登录token不能为空."
),
-
USERID_NOT_UNAUTHORIZED(
3003
,
"用户token或ID验证不通过"
),
-
RESPONSE_CODE_UNLOGIN_ERROR(
421
,
"未登录异常"
),
-
LOGIN_TIME_EXP(
3004
,
"登录时间超长,请重新登录"
);
-
-
-
private
int
code;
-
private
String message;
-
-
-
private
LoginResponseCode(
int
code,String message) {
-
this
.code = code;
-
this
.message = message;
-
}
-
public
int
getCode() {
-
return
code;
-
}
-
public
void
setCode(
int
code) {
-
this
.code = code;
-
}
-
public
String getMessage() {
-
return
message;
-
}
-
public
void
setMessage(String message) {
-
this
.message = message;
-
}
-
-
public
static
ResponseVO buildEnumResponseVO(LoginResponseCode responseCode, Object data) {
-
return
new
ResponseVO(responseCode.getCode(),responseCode.getMessage(),data);
-
}
-
-
public
static
Map<String, Object> buildReturnMap(LoginResponseCode responseCode, Object data) {
-
Map<String, Object> map =
new
HashMap<String, Object>();
-
map.put(
"code"
, responseCode.getCode());
-
map.put(
"message"
, responseCode.getMessage());
-
map.put(
"data"
, data);
-
return
map;
-
}
-
}
8. 编写统一sso单点登录接口:
-
@RequestMapping
(value =
"/login"
, method = RequestMethod.POST)
-
public
Map<String, Object> login(
@RequestBody
JSONObject json){
-
String loginName = json.optString(
"loginName"
);
-
String password = json.optString(
"password"
);
-
-
if
(StringUtils.isEmpty(loginName)){
-
return
MemberResponseCode.buildReturnMap(MemberResponseCode.RESPONSE_CODE_USER_NAME_IS_NOT_EMPTY,
null
);
-
}
-
-
if
(StringUtils.isEmpty(password)){
-
return
MemberResponseCode.buildReturnMap(MemberResponseCode.RESPONSE_CODE_PWD_CAN_NOT_BE_EMPTY,
null
);
-
}
-
-
User user = systemService.getBaseUserByLoginName(loginName);
-
-
if
(
null
== user){
-
return
MemberResponseCode.buildReturnMap(MemberResponseCode.RESPONSE_CODE_USER_VALIDATE_NO_SUCCESS,
false
);
-
}
-
boolean
isValidate = systemService.validatePassword(password, user.getPassword());
-
if
(!isValidate){
-
return
MemberResponseCode.buildReturnMap(MemberResponseCode.RESPONSE_CODE_USER_VALIDATE_NO_SUCCESS,
false
);
-
}
-
if
(isValidate){
-
-
Login login =
new
Login(user.getId(), user.getLoginName(), user.getPassword());
-
-
String token = JWT.sign(login, 60L* 1000L* 30L);
-
Map<String,Object> result =
new
HashMap<String,Object>();
-
result.put(
"loginToken"
, token);
-
result.put(
"userId"
, user.getId());
-
result.put(
"user"
, user);
-
-
-
-
-
this
.rebuildLoginUser(user.getId(), token);
-
return
ResponseCode.buildReturnMap(ResponseCode.RESPONSE_CODE_LOGIN_SUCCESS, result);
-
}
-
-
return
ResponseCode.buildReturnMap(ResponseCode.USER_LOGIN_PWD_ERROR,
false
);
-
}
9. 测试sso单点登录:
返回结果集:
-
{
-
"message"
:
"用户登录成功"
,
-
"data"
: {
-
"loginToken"
:
"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1MDkzODA1OTU0NTksInBheWxvYWQiOiJ7XCJ1aWRcIjpcIjExXCIsXCJsb2dpbk5hbWVcIjpcImFkbWluXCIsXCJwYXNzd29yZFwiOlwiZjU0NGQxM2QyY2EwNDU5ZGQ0ZTU1NzVjNmZkYWIzMzM0MzE1MWFlZjgwYmE5ZTNiN2U1ZjM2MzJcIn0ifQ.56L60WtxHXSu9vNs6XsWy5zbmc3kP_IWG1YpReK50DM"
,
-
"userId"
:
"11"
,
-
"user"
: {
-
"QQ"
:
"2147775633"
,
-
"id"
:
"11"
,
-
"isNewRecord"
:
false
,
-
"remarks"
:
""
,
-
"createDate"
:
"2017-08-08 08:08:08"
,
-
"updateDate"
:
"2017-10-29 11:23:50"
,
-
"loginName"
:
"admin"
,
-
"no"
:
"00012"
,
-
"name"
:
"admin"
,
-
"email"
:
"2147775633@qq.com"
,
-
"phone"
:
"400000000"
,
-
"mobile"
:
"13888888888"
,
-
"userType"
:
""
,
-
"loginIp"
:
"0:0:0:0:0:0:0:1"
,
-
"loginDate"
:
"2017-10-30 10:48:06"
,
-
"loginFlag"
:
"1"
,
-
"photo"
:
""
,
-
"idCard"
:
"420888888888888888"
,
-
"oldLoginIp"
:
"0:0:0:0:0:0:0:1"
,
-
"oldLoginDate"
:
"2017-10-30 10:48:06"
,
-
"roleNames"
:
""
,
-
"admin"
:
false
-
}
-
},
-
"code"
:
200
-
}
到此完毕!!
用java实施的电子商务平台太少了,使用spring cloud技术构建的b2b2c电子商务平台更少,大型企业分布式互联网电子商务平台,推出PC+微信+APP+云服务的云商平台系统,其中包括B2B、B2C、C2C、O2O、新零售、直播电商等子平台。
Spring Cloud大型企业分布式微服务云构建的B2B2C电子商务平台(企业架构源码可以加求球:叁五三陆二肆柒二伍玖)
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69984376/viewspace-2733120/
,如需转载,请注明出处,否则将追究法律责任。