采用流量较大的第三方,使用第三方登录,可以更好的留住用户
第三方登录其实是比较简单的,其实第三方已经给我们提供好了服务,实现第三方登录,我们只需要调用第三方给我们提供的接口,发送请求即可。不论QQ、微信还是微博,实现第三方登录的原理都是一样的,可能就是发送的url路径不一样、路径内的参数可能不一样,其他的操作步骤都是一样的
OAuth2.0即Open Authorization,是身份认证的意思,所有的第三方登录都需要实现OAuth2.0原理。
package cn.itsource.crm.shiro;
import cn.itsource.crm.domain.Employee;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
public enum UserContext {
Utils;
//准备一个常量
private static final String USERNAME_IN_SESSION ="sessionUser";
//将当前用户存入session
public void setUser(Employee employee){
//获取当前对象
Subject subject = SecurityUtils.getSubject();
//将员工对象放入session
subject.getSession().setAttribute(USERNAME_IN_SESSION,employee);
}
//将用户从session中取出
public Employee getUser(){
//获取当前对象
Subject subject = SecurityUtils.getSubject();
//从session中拿值
return (Employee)subject.getSession().getAttribute(USERNAME_IN_SESSION);
}
}
package cn.itsource.crm.shiro;
//登录类型,分为微信登录和普通系统用户登录
public enum LoginType {
WechatLogin("Wechat"),EmployeeLogin("Employee");
private String loginType;
LoginType(String loginType) {
this.loginType = loginType;
}
public String getLoginType() {
return loginType;
}
public void setLoginType(String loginType) {
this.loginType = loginType;
}
}
package cn.itsource.crm.shiro;
import cn.itsource.crm.domain.Employee;
import cn.itsource.crm.service.EmployeeService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Map;
public class WechatRealm extends AuthorizingRealm {
@Autowired
private EmployeeService employeeService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
/**
* 身份认证
* @param token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//① 获取code
CRMToken dmsToken = (CRMToken) token;
String code = dmsToken.getUsername();
//② 获取token
String url = WechatConstant.TOKEN_URI.replaceAll("APPID",WechatConstant.APPID)
.replaceAll("SECRET",WechatConstant.SECRET)
.replaceAll("CODE",code);
//借助httpClicen工具类,(使用的是java代码)发送请求,获取令牌
String respStr = HttpClientUtils.doGet(url);
Map respMap = HttpClientUtils.parseRespJson(respStr);
String access_token = respMap.get("access_token");
String openid = respMap.get("openid");
//③ unionid
url = WechatConstant.USER_INFO_URI.replaceAll("ACCESS_TOKEN",access_token)
.replaceAll("OPENID",openid);
respStr = HttpClientUtils.doGet(url);
String unionid = HttpClientUtils.parseRespJson(respStr).get("unionid");
//查询数据库
Employee employee = employeeService.getByUnionId(unionid);
if(employee==null){
SecurityUtils.getSubject().getSession().setAttribute("unionid",unionid);
throw new NoUserBindingException();
}
SimpleAuthenticationInfo info
= new SimpleAuthenticationInfo(employee,code,getName());
return info;
}
}
package cn.itsource.crm.web.controller;
import cn.itsource.basic.util.Message;
import cn.itsource.crm.domain.Employee;
import cn.itsource.crm.shiro.*;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
@Controller
public class LoginController {
//用于完成跳转
@RequestMapping(value="/login",method = RequestMethod.GET)
public String index(Model model){
String url = WechatConstant.CODE_URL.replaceAll("APPID",WechatConstant.APPID)
.replaceAll("REDIRECT_URI",WechatConstant.REDIRECT_URI);
model.addAttribute("authenUrl",url);
return "forward:/WEB-INF/views/login.jsp";
}
/**
* 微信登录用户授权后的回调
* @param code
* @return
*/
@RequestMapping(value = "/callback",method = RequestMethod.GET)
public String callback(String code){
//code身份认证
Subject currentUser = SecurityUtils.getSubject();
CRMToken token = new CRMToken(code,code, LoginType.WechatLogin);
try {
currentUser.login(token);
Employee employee = (Employee) currentUser.getPrincipal();
UserContext.Utils.setUser(employee);
}catch (NoUserBindingException e){
e.printStackTrace();
return "bind";
}catch (AuthenticationException e) {
e.printStackTrace();
return "forward:/login";
}
//获取用户身份存入到session中
return "main";
}
//登陆方法
@RequestMapping(value = "/login", method = RequestMethod.POST)
@ResponseBody
public Message login(String username, String password, String validation, HttpServletRequest req) {
System.out.println("======================================="+username);
System.out.println("======================================="+password);
//拿到当前用户
Subject subject = SecurityUtils.getSubject();
if(!subject.isAuthenticated()) {
try {
//准备令牌,使用的是自定义的令牌
UsernamePasswordToken token = new CRMToken(username, password,LoginType.EmployeeLogin);
//登陆
subject.login(token);
//将当前用户存入session
Employee employee = (Employee) subject.getPrincipal();
UserContext.Utils.setUser(employee);
} catch (UnknownAccountException e) {
e.printStackTrace();
System.out.println("用户名错误");
return new Message(false, "用户名或密码错误!");
} catch (IncorrectCredentialsException e) {
e.printStackTrace();
System.out.println("密码错误");
return new Message(false, "用户名或密码错误!");
} catch (AuthenticationException e) {
e.printStackTrace();
System.out.println("未知错误");
return new Message(false, "发生了未知错误!请联系管理员");
}
}
return new Message();
}
//登出功能
@RequestMapping("/logout")
public String logout() {
System.out.println("我登出了");
//拿到当前用户
Subject subject = SecurityUtils.getSubject();
//登出
subject.logout();
return "login";
}
}
package cn.itsource.crm.web.controller;
import cn.itsource.crm.domain.Employee;
import cn.itsource.crm.service.EmployeeService;
import cn.itsource.crm.shiro.CRMToken;
import cn.itsource.crm.shiro.LoginType;
import cn.itsource.crm.shiro.UserContext;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class BindController {
@Autowired
private EmployeeService employeeService;
@RequestMapping(value = "/bind",method = RequestMethod.POST)
public String bindUser(String username,String password){
//查询用户名和密码是否正确
//认证
Subject currentUser = SecurityUtils.getSubject();
CRMToken token = new CRMToken(username,password, LoginType.EmployeeLogin);
try {
currentUser.login(token);
//认证成功,添加unionid
Employee employee = (Employee) currentUser.getPrincipal();
String unionid = (String) currentUser.getSession().getAttribute("unionid");
employeeService.bindWechat(employee.getId(),unionid);
//把用户信息存到session中
UserContext.Utils.setUser(employee);
} catch (AuthenticationException e) {
//认证失败,返回登录页面
e.printStackTrace();
return "forward:/login";
}
return "main";
}
}