sso單點登陸概念前端
1.一處登陸,到處登陸.會單獨作一個單點登陸系統,只負責頒發token和驗證token,和頁面登陸功能.nginx
2.經過在瀏覽器cookie中放入token,和在redis中對應token放入用戶信息的方式,代替session共享,使用jwt(json web token)自定義一個攜帶用戶信息token加密算法.web
3.cookie中的token是已經使用過的token,取名oldToken . url地址欄中的token新頒發的token,取名newTokenredis
作法:算法
1.首先自定義一個註解,做用在方法上.有3種狀態:spring
1.1 若是爲null,表示直接放行,例如商品詳情頁,用戶能夠不用登陸直接訪問.數據庫
1.2 若是爲false,常常用在購物車方法上,表示可登陸可不登陸,用戶不登陸購物車數據保存cookie中,用戶登陸數據保存數據庫並放redis一份.json
1.3 若是爲true,表示用戶必須登陸,例如購物車結算跳訂單頁面.api
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginRequired {瀏覽器
boolean isNeededSuccess() default true;
}
2.自定義攔截器,並加入到springr容器中
@Configuration
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
@Autowired
AuthInterceptor authInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry){
registry.addInterceptor(authInterceptor).addPathPatterns("/**");
super.addInterceptors(registry);
}
}
@Component
public class AuthInterceptor extends HandlerInterceptorAdapter {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod hm = (HandlerMethod)handler;
LoginRequired methodAnnotation = hm.getMethodAnnotation(LoginRequired.class);
if(methodAnnotation==null){
return true;
}else{
// 先得到用戶cookie中關於用戶的身份token
//token有四種狀況
String token = "";
String oldToken = CookieUtil.getCookieValue(request, "oldToken", true);
if(StringUtils.isNotBlank(oldToken)){
token = oldToken;
}
String newToken = request.getParameter("newToken");
if(StringUtils.isNotBlank(newToken)){
token = newToken;
}
if(StringUtils.isNotBlank(token)){
// 驗證用戶的token是否正確
// 經過遠程ws請求認證中心,驗證token
String requestUrl = "http://passport.gmall.com:8090/verify?token="+token+"¤tIp="+request.getRemoteAddr();
String successJSON = HttpclientUtil.doGet(requestUrl);
HashMap<String,String> hashMap = new HashMap<>();
HashMap hashMapJSON = JSON.parseObject(successJSON, hashMap.getClass());
if(hashMapJSON!=null&&hashMapJSON.get("success").equals("success")){
// 從新更新cookie的過時時間
CookieUtil.setCookie(request,response,"oldToken",token,60*60,true);
request.setAttribute("memberId",hashMapJSON.get("memberId"));
request.setAttribute("nickname",hashMapJSON.get("nickname"));
return true;
}else{
if(methodAnnotation.isNeededSuccess()){
String ReturnUrl = request.getRequestURL().toString();
response.sendRedirect("http://passport.gmall.com:8090/index?ReturnUrl="+ReturnUrl);
// 攔截驗證
return false;
}
}
}else{
if(methodAnnotation.isNeededSuccess()) {
String ReturnUrl = request.getRequestURL().toString();
response.sendRedirect("http://passport.gmall.com:8090/index?ReturnUrl=" + ReturnUrl);
// 攔截驗證
return false;
}
}
}
return true;
}
}
3.單點登陸系統代碼
@Controller
public class passportController {
@Reference
UserService userService;
@RequestMapping("vlogin")
public String vlogin(String code, HttpServletRequest request) {
// 換取access_token
String access_token_url = "https://api.weibo.com/oauth2/access_token?client_id= 25920146&client_secret=dc8de1392f642a01259b136ff8e970b9&grant_type=authorization_code&redirect_uri=http://passport.gmall.com:8090/vlogin&code=e211655dd6c78a66fcfcfdff552424f6";
Map<String,String> map = new HashMap<String,String>();
map.put("client_id","25920146");
map.put("client_secret","dc8de1392f642a01259b136ff8e970b9");
map.put("grant_type","authorization_code");
map.put("redirect_uri","http://passport.gmall.com:8090/vlogin");
map.put("code",code);
String access_json = HttpclientUtil.doPost("https://api.weibo.com/oauth2/access_token", map);
System.out.println(access_json);
Map<String,String> map_access_json = new HashMap<String,String>();
Map access_map = JSON.parseObject(access_json, map_access_json.getClass());
// 得到第三方用戶數據
String access_token = (String)access_map.get("access_token");
String uid = (String)access_map.get("uid");// uid uidStr
UmsMember umsMember = new UmsMember();
umsMember = userService.isUidExists(uid);
if(umsMember==null){
String show_url = "https://api.weibo.com/2/users/show.json?access_token="+access_token+"&uid="+uid;
String user_json = HttpclientUtil.doGet(show_url);
Map<String,String> map_user_json = new HashMap<String,String>();
Map user_map = JSON.parseObject(user_json, map_user_json.getClass());
System.out.println(user_map);
// 存入數據庫
umsMember.setNickname((String)user_map.get("screen_name"));
umsMember.setUsername((String)user_map.get("name"));
umsMember.setSourceType("2");
umsMember.setSourceUid((String)user_map.get("idstr"));
umsMember.setCreateTime(new Date());
umsMember.setAccessToken(access_token);
umsMember.setAccessCode(code);
umsMember = userService.addUser(umsMember);
}
// 根據用戶信息生成token
String key = "houruisso";
String ip = request.getRemoteAddr();
Map<String,Object> token_map = new HashMap<>();
token_map.put("nickname",umsMember.getNickname());
token_map.put("memberId",umsMember.getId());
String token = JwtUtil.encode(key, token_map, ip);
// 將生成的token和登陸用戶信息保存在緩存中一分
userService.addUserCache(token,umsMember);
return "redirect:http://search.gmall.com:8083/index?newToken="+token;
}
@RequestMapping("login")
@ResponseBody
public String login(UmsMember umsMember, HttpServletRequest request) {
System.out.println("用戶登陸,驗證用戶名和密碼是否正確");
// 調用用戶服務userService,驗證用戶名和密碼
UmsMember umsMemberFromDb = userService.login(umsMember);
if (umsMemberFromDb == null) {
return "fail";
} else {
// 根據已經登陸的用戶信息和,服務器密鑰,和其餘鹽值(根據系統算法)生成一個token
String key = "atguigusso";
String ip = request.getRemoteAddr();
//String ip = request.getHeader("x-forward-for");//nginx
Map<String, Object> map = new HashMap<>();
map.put("nickname", umsMemberFromDb.getNickname());
map.put("memberId", umsMemberFromDb.getId());
String token = JwtUtil.encode(key, map, ip);
// 將生成的token和登陸用戶信息保存在緩存中一分
userService.addUserCache(token,umsMemberFromDb);
return token;
}
}
@RequestMapping("verify")
@ResponseBody
public String verify(String token, String currentIp) {
System.out.println("認證中心認證用戶的token");
String key = "houruisso";
String ip = currentIp;
Map<String, Object> map = JwtUtil.decode(token, key, ip);
Map<String,String> verifyReturn = new HashMap<>();
if (map != null) {
verifyReturn.put("success","success");
verifyReturn.put("memberId",(String)map.get("memberId"));
verifyReturn.put("nickname",(String)map.get("nickname"));
return JSON.toJSONString(verifyReturn);
} else {
verifyReturn.put("success","fail");
return JSON.toJSONString(verifyReturn);
}
}
@RequestMapping("index")
public String index(String ReturnUrl, ModelMap modelMap) {
System.out.println("認證中心首頁");
modelMap.put("ReturnUrl", ReturnUrl);
return "index";
}
}
4.前端代碼
<!--底部-->
<input type="text" id="ReturnUrl" th:value="${ReturnUrl}"/><br />
</body>
<script language="JavaScript">
function submitLogin() {
var username = $("#username").val();
var password = $("#password").val();
$.post("login",{username:username,password:password},function(token){
if(token=="fail"){
alert("登陸失敗,用戶名和密碼錯誤");
}else{
window.location.href=$("#ReturnUrl").val()+"?newToken="+token;
}
});
}
@Overridepublic void addUserCache(String token, UmsMember umsMemberFromDb) { String tokenKey = "user:"+umsMemberFromDb.getId()+":token"; String userKey = "user:"+umsMemberFromDb.getId()+":info"; Jedis jedis = null; jedis = redisUtil.getJedis(); jedis.setex(tokenKey,60*60,token); jedis.setex(userKey,60*60, JSON.toJSONString(umsMemberFromDb)); jedis.close(); }