这篇博文主要记录我实现扫码登录的步骤,及代码。其实之前从网上搜了很久扫码登录,copy版很多,所以回答也就那几种。
我把我自己的思路也说一下吧。如果有更好的意见或者我有不对的地方,请指出。
首先说一下我用的技术, maven构建项目,ssh ,redis, shiro, goeasy推送,ZXing二维码生成。
图有点粗糙。凑合看,我解释一下,首先我认为,session肯定是浏览器和服务器交互唯一的标识,所以我认为sessionid可以直接定位到是哪一个用户进行扫码登录,
OK,那么围绕这一点,当浏览器打开我们的网站时,我们就getsession,这样保证先给浏览器一个session对象。
接下来我将用代码展示的方式给大家展示一下流程
首先从网上扒了一个监听全局session的监听器。
- public class MySessionContext {
- private static HashMap mymap = new HashMap();
- public static synchronized void AddSession(HttpSession session) {
- if (session != null) {
- mymap.put(session.getId(), session);
- }
- }
- public static synchronized void DelSession(HttpSession session) {
- if (session != null) {
- mymap.remove(session.getId());
- }
- }
- public static synchronized HttpSession getSession(String session_id) {
- if (session_id == null)
- return null;
- return (HttpSession) mymap.get(session_id);
- }
- }
public class MySessionContext {
private static HashMap mymap = new HashMap();
public static synchronized void AddSession(HttpSession session) {
if (session != null) {
mymap.put(session.getId(), session);
}
}
public static synchronized void DelSession(HttpSession session) {
if (session != null) {
mymap.remove(session.getId());
}
}
public static synchronized HttpSession getSession(String session_id) {
if (session_id == null)
return null;
return (HttpSession) mymap.get(session_id);
}
}
- public class MySessionListener {
- public void sessionCreated(HttpSessionEvent httpSessionEvent) {
- MySessionContext.AddSession(httpSessionEvent.getSession());
- }
- public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
- HttpSession session = httpSessionEvent.getSession();
- MySessionContext.DelSession(session);
- }
- }
public class MySessionListener {
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
MySessionContext.AddSession(httpSessionEvent.getSession());
}
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
HttpSession session = httpSessionEvent.getSession();
MySessionContext.DelSession(session);
}
}
- <listener>
- <listener-class>cn.itcast.web.action.lisener.MySessionListener</listener-class>
- </listener>
<listener>
<listener-class>cn.itcast.web.action.lisener.MySessionListener</listener-class>
</listener>
主要是因为不知道如何通过sessionid获得session对象。。知道的请留言告诉我,感激不尽!
登录Action
- @Action(“loginAction_login”)
- public String login() throws Exception {
- Map<String, Object> session = ActionContext.getContext().getSession();
- Subject subject = SecurityUtils.getSubject();//shiro
- if(session.get(SysConstant.CURRENT_USER_INFO)!=null){//如果有session用户信息,直接走验证,说明是手机扫码的
- User user = (User) session.get(SysConstant.CURRENT_USER_INFO);
- subject.login(new UsernamePasswordToken(user.getUserName(),user.getPassword()));
- }
- if(subject.isAuthenticated()){//验证成功直接返回成功。
- return SUCCESS;
- }
- if(UtilFuns.isEmpty(username)){//为浏览器分配session
- HttpServletRequest request = ServletActionContext.getRequest();
- sessID = request.getSession().getId();
- super.push(sessID);
- request.getSession().setAttribute(”ssid”, sessID);
- return “login”;
- }
- try {//登录成功赋值session
- String md5 = Encrypt.md5(password, username);
- subject.login(new UsernamePasswordToken(username, md5));
- session.put(SysConstant.CURRENT_USER_INFO, (User)subject.getPrincipal());
- } catch (Exception e) {
- e.printStackTrace();
- request.put(”errorInfo”, “对不起,登录失败,用户名或密码错误!!!”);
- HttpServletRequest request = ServletActionContext.getRequest();
- sessID = request.getSession().getId();
- super.push(sessID);//压栈
- request.getSession().setAttribute(”ssid”, sessID);//为了获得图片二维码时有session
- return “login”;
- }
- User user =(User)subject.getPrincipal();
- //4.将user对象保存到session域中
- session.put(SysConstant.CURRENT_USER_INFO, user);
- //5.跳页面
- return SUCCESS;
- }
@Action("loginAction_login")
public String login() throws Exception {
Map<String, Object> session = ActionContext.getContext().getSession();
Subject subject = SecurityUtils.getSubject();//shiro
if(session.get(SysConstant.CURRENT_USER_INFO)!=null){//如果有session用户信息,直接走验证,说明是手机扫码的
User user = (User) session.get(SysConstant.CURRENT_USER_INFO);
subject.login(new UsernamePasswordToken(user.getUserName(),user.getPassword()));
}
if(subject.isAuthenticated()){//验证成功直接返回成功。
return SUCCESS;
}
if(UtilFuns.isEmpty(username)){//为浏览器分配session
HttpServletRequest request = ServletActionContext.getRequest();
sessID = request.getSession().getId();
super.push(sessID);
request.getSession().setAttribute("ssid", sessID);
return "login";
}
try {//登录成功赋值session
String md5 = Encrypt.md5(password, username);
subject.login(new UsernamePasswordToken(username, md5));
session.put(SysConstant.CURRENT_USER_INFO, (User)subject.getPrincipal());
} catch (Exception e) {
e.printStackTrace();
request.put("errorInfo", "对不起,登录失败,用户名或密码错误!!!");
HttpServletRequest request = ServletActionContext.getRequest();
sessID = request.getSession().getId();
super.push(sessID);//压栈
request.getSession().setAttribute("ssid", sessID);//为了获得图片二维码时有session
return "login";
}
User user =(User)subject.getPrincipal();
//4.将user对象保存到session域中
session.put(SysConstant.CURRENT_USER_INFO, user);
//5.跳页面
return SUCCESS;
}
这里实际上页面也悄悄工作了
- <script>
- function changeCode(){
- ("#erweima").attr('src','{ctx}/mobile/MobileCode?t=’+new Date().getTime());//选择二维码时直接去替换二维码图片
- }
- </script>
- <script type=“text/javascript” src=“http://cdn.goeasy.io/goeasy.js”></script>
- <script type=“text/javascript”>
- var goEasy = new GoEasy({
- appkey: ‘BC-c9196bffff9b4fcabd70a200f95a51d2’
- });
- goEasy.subscribe({
- channel: ’${sessID }’,//这里绑定sessionid保证推送是指定用户
- onMessage: function(message){
- location.reload()
- }
- });
- </script>
- </head>
<script>
function changeCode(){
$("#erweima").attr('src','${ctx}/mobile/MobileCode?t='+new Date().getTime());//选择二维码时直接去替换二维码图片
}
</script>
<script type="text/javascript" src="http://cdn.goeasy.io/goeasy.js"></script>
<script type="text/javascript">
var goEasy = new GoEasy({
appkey: 'BC-c9196bffff9b4fcabd70a200f95a51d2'
});
goEasy.subscribe({
channel: '${sessID }',//这里绑定sessionid保证推送是指定用户
onMessage: function(message){
location.reload()
}
});
</script>
</head>
- public class QRCodeUtil extends BaseAction{
- @Action(value=“MobileCode”)
- public void getcode(){//生成二维码
- HttpServletResponse response;
- String urls;
- Hashtable<EncodeHintType, String> hints;
- BitMatrix matrix;
- try {
- response = (HttpServletResponse)
- ActionContext.getContext().get(org.apache.struts2.StrutsStatics.HTTP_RESPONSE);
- Jedis jedis =new Jedis(“192.168.123.101”);
- HttpServletRequest request = ServletActionContext.getRequest();
- String sessID = (String) session.get(”ssid”);
- String id = request.getSession().getId();
- MySessionContext.AddSession(request.getSession());
- String key = Encrypt.md5(UUID.randomUUID().toString(), ”cao”);
- jedis.set(key, sessID, ”NX”, “EX”, 1800);
- urls = ”http://192.168.123.96:8080/test/mobileAutoLog?parms=”+key;//这是手机端扫码的结果
- hints = new Hashtable<EncodeHintType, String>();
- hints.put(EncodeHintType.CHARACTER_SET, ”UTF-8”);
- matrix = null;
- matrix = new MultiFormatWriter().encode(urls,
- BarcodeFormat.QR_CODE, 300, 300, hints);
- MatrixToImageWriter.writeToStream(matrix, ”jpg”, response.getOutputStream());
- } catch (Exception e1) {
- // TODO Auto-generated catch block
- e1.printStackTrace();
- }
- }
public class QRCodeUtil extends BaseAction{
@Action(value="MobileCode")
public void getcode(){//生成二维码
HttpServletResponse response;
String urls;
Hashtable<EncodeHintType, String> hints;
BitMatrix matrix;
try {
response = (HttpServletResponse)
ActionContext.getContext().get(org.apache.struts2.StrutsStatics.HTTP_RESPONSE);
Jedis jedis =new Jedis("192.168.123.101");
HttpServletRequest request = ServletActionContext.getRequest();
String sessID = (String) session.get("ssid");
String id = request.getSession().getId();
MySessionContext.AddSession(request.getSession());
String key = Encrypt.md5(UUID.randomUUID().toString(), "cao");
jedis.set(key, sessID, "NX", "EX", 1800);
urls = "http://192.168.123.96:8080/test/mobileAutoLog?parms="+key;//这是手机端扫码的结果
hints = new Hashtable<EncodeHintType, String>();
hints.put(EncodeHintType.CHARACTER_SET, "UTF-8");
matrix = null;
matrix = new MultiFormatWriter().encode(urls,
BarcodeFormat.QR_CODE, 300, 300, hints);
MatrixToImageWriter.writeToStream(matrix, "jpg", response.getOutputStream());
} catch (Exception e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
然后假装扫码成功
。
。
。
。
。
。
我不得不插一段安卓代码了。。。
- public void ineedLogin(View v){//扫码成功
- SharedPreferences sharedPreferences = getSharedPreferences(”userinfo”, 1);
- token = sharedPreferences.getString(”username”, “”);
- try {
- GetDataFromService();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- private void GetDataFromService() throws Exception{
- RequestParams params = new RequestParams(result+“&username=”+token);//去访问浏览器发过来的请求,并且拼接上用户的用户名。
- x.http().post(params, new Callback.CacheCallback<String>() {
- @Override
- public boolean onCache(String result) {
- return false;
- }
- @Override
- public void onSuccess(String result) {
- }
- @Override
- public void onError(Throwable ex, boolean isOnCallback) {
- }
- @Override
- public void onCancelled(CancelledException cex) {
- }
- @Override
- public void onFinished() {
- }
- });
public void ineedLogin(View v){//扫码成功
SharedPreferences sharedPreferences = getSharedPreferences("userinfo", 1);
token = sharedPreferences.getString("username", "");
try {
GetDataFromService();
} catch (Exception e) {
e.printStackTrace();
}
}
private void GetDataFromService() throws Exception{
RequestParams params = new RequestParams(result+"&username="+token);//去访问浏览器发过来的请求,并且拼接上用户的用户名。
x.http().post(params, new Callback.CacheCallback<String>() {
@Override
public boolean onCache(String result) {
return false;
}
@Override
public void onSuccess(String result) {
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
}
@Override
public void onCancelled(CancelledException cex) {
}
@Override
public void onFinished() {
}
});
其实我这里选了一个比较low的。。我为了省事,安卓直接拼了username过去。。还用了个Xutils…搞得重写了很多方法。。别笑我。。哈哈哈哈
好了我们再来看扫码成功的action
- Jedis jedis =new Jedis(“192.168.123.101”);
- String jedisParms = jedis.get(parms);
- if(jedisParms!=null&&!jedisParms.equals(“”)){//去看redis是否有手机端传过来这个码,
- List<User> find = userService.find(new Specification<User>() {
- @Override
- public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
- return cb.equal(root.get(“userName”).as(String.class),username);
- }
- });
- User u1 = find.iterator().next();//拿到用户信息
- HttpSession session = MySessionContext.getSession(jedisParms);//拿到session
- session.setAttribute(SysConstant.CURRENT_USER_INFO, u1);
- final String APP_KEY=“BC-c9196bffff9b4fcabd70a200f95a51d2”;
- GoEasy goEasy =new GoEasy(APP_KEY);
- goEasy.publish(jedisParms,”heh” ); //直接推送前端
- return NONE;
- }else{
- System.out.println(”验证失败”);
- }
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- return “error”;
- }
Jedis jedis =new Jedis("192.168.123.101");
String jedisParms = jedis.get(parms);
if(jedisParms!=null&&!jedisParms.equals("")){//去看redis是否有手机端传过来这个码,
List<User> find = userService.find(new Specification<User>() {
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
return cb.equal(root.get("userName").as(String.class),username);
}
});
User u1 = find.iterator().next();//拿到用户信息
HttpSession session = MySessionContext.getSession(jedisParms);//拿到session
session.setAttribute(SysConstant.CURRENT_USER_INFO, u1);
final String APP_KEY="BC-c9196bffff9b4fcabd70a200f95a51d2";
GoEasy goEasy =new GoEasy(APP_KEY);
goEasy.publish(jedisParms,"heh" ); //直接推送前端
return NONE;
}else{
System.out.println("验证失败");
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return "error";
}
前端收到推送之后会刷新页面,并且session里面已经有值,那么会走登录验证的方法,session有值的方法。
我们再来看shiro,这里当时我有点懵,因为shiro实际上我用的不是很熟悉,就知道它运行的流程,当时不知道怎么才能让他验证通过,所以走了很多次shiro的登录方法,如果有别的好办法!也希望大家告诉我!!!
- protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
- Map<String, Object> session = ActionContext.getContext().getSession();
- User object = (User) session.get(SysConstant.CURRENT_USER_INFO);
- if(object!=null){//手机
- final String username = object.getUserName();
- List<User> findUser = userService.find(new Specification<User>() {
- @Override
- public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
- // TODO Auto-generated method stub
- return cb.equal(root.get(“userName”).as(String.class), username);
- }
- });
- return new SimpleAuthenticationInfo(findUser,findUser.get(0).getPassword(),this.getName());
- }else{//电脑
- UsernamePasswordToken userNamePassword=(UsernamePasswordToken) arg0;
- final String username = userNamePassword.getUsername();
- List<User> findUser = userService.find(new Specification<User>() {
- @Override
- public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
- // TODO Auto-generated method stub
- return cb.equal(root.get(“userName”).as(String.class), username);
- }
- });
- if(findUser!=null && findUser.size()!=0){
- return new SimpleAuthenticationInfo(findUser,findUser.get(0).getPassword(),this.getName());
- }
- }
- return null;
- }
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
Map<String, Object> session = ActionContext.getContext().getSession();
User object = (User) session.get(SysConstant.CURRENT_USER_INFO);
if(object!=null){//手机
final String username = object.getUserName();
List<User> findUser = userService.find(new Specification<User>() {
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
// TODO Auto-generated method stub
return cb.equal(root.get("userName").as(String.class), username);
}
});
return new SimpleAuthenticationInfo(findUser,findUser.get(0).getPassword(),this.getName());
}else{//电脑
UsernamePasswordToken userNamePassword=(UsernamePasswordToken) arg0;
final String username = userNamePassword.getUsername();
List<User> findUser = userService.find(new Specification<User>() {
@Override
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
// TODO Auto-generated method stub
return cb.equal(root.get("userName").as(String.class), username);
}
});
if(findUser!=null && findUser.size()!=0){
return new SimpleAuthenticationInfo(findUser,findUser.get(0).getPassword(),this.getName());
}
}
return null;
}
因为之前没有加扫码登录时,我HashMd5是在这个密码比较器里加的,加了扫码之后,页面出过来明文我就直接加密了。所以密码比较器里面,直接比较就可以了
- public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
- UsernamePasswordToken uToken =(UsernamePasswordToken) token;
- String en =new String(uToken.getPassword());
- return equals(en, info.getCredentials());
- }
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
UsernamePasswordToken uToken =(UsernamePasswordToken) token;
String en =new String(uToken.getPassword());
return equals(en, info.getCredentials());
}
到此java扫码登录完成!
如果各位有别的好思路,希望也分享给我。
文章转载至:http://blog.csdn.net/lawsonjin/article/details/76856025