最近公司要原本springmvc+shiro 权限控制的一个项目,改为前后端分离的,使前端人员能有更多时间来做前端的交互工作,提升用户体验。
但是这个问题就来了,原来没有分离的情况下,shiro 权限是通过凭证来登录的,现在需要更改为前端ajax提交登录信息来登录,于是更改了shiro的登录方式,这里做一个记录。
@RequestMapping(value="/userLogin",method=RequestMethod.GET)
@ResponseBody
public ResultSuperApp superAppLogin(HttpServletRequest request) throws Exception{
String username = request.getParameter("username");
String password = request.getParameter("password");
String error="未知错误异常";
if(null == username || null == password ){
return ResultSuperApp.getFailureInstance("参数为空", 300);
}
try {
Subject currentUser = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username,password, false,request.getRemoteAddr());
currentUser.login(token);
Subject subject = SecurityUtils.getSubject();
String sessionId = (String) subject.getSession().getId();
/*//无Redis服务器,临时性解决token
String Json = new Gson().toJson(token);
byte[] encodeBase64 = Base64.encodeBase64(Json.getBytes("UTF-8"));
String base64 = new String(encodeBase64);//Base64值*/
ActiveUser activeUser = ShiroUtil.getActiveUser();
return ResultSuperApp.getSuccess(activeUser, "登录成功", 200, sessionId);
//SecurityUtils.getSubject().getSession().setTimeout(-1000l);
} catch (Exception e) {
//根据与异常信息抛出对应的异常
if(e.getClass().getName()!=null){
if(UnknownAccountException.class.getName().equals(e.getClass().getName())){
//抛出账号不存在异常
error="账号不存在";
}else if(IncorrectCredentialsException.class.getName().equals(e.getClass().getName())){
//
//throw new CustomException("密码错误");
error = "用户名密码错误";
}else{
//密码错误
//throw new CustomException("未知错误异常");
error = "未知错误异常";
}
}
return ResultSuperApp.getFailureInstance(error, 499);
}
}
注意:以上代码注释掉的部分是返回登录的token信息,之后每次前端请求的时候,参数都需要带着这个token。
放开的代码片段是登录后,返回shiro的sessionid,前端在cookie里保存一下这个sessionid,每次请求的时候参数中都需要加上sessionid。
两种方式都可以使用,但是本人推荐返回sessionid 的方式,因为前端给了sessionid 之后,我们只需要在服务中通过sessionid 获取shiro的session信息,然后就能获取到shiro对应的用户信息。而使用token的这种方式的话,每次前端请求过来,后端都需要根据这个token再登录一下。两者的区别就在这里。
获取用户信息(根据sessionid)
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.SessionKey;
import org.apache.shiro.subject.SimplePrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.apache.shiro.web.session.mgt.WebSessionKey;
import org.springframework.beans.factory.annotation.Autowired;
import com.eaju.bos.dao.mapper.DownloadCenterMapper;
import com.eaju.bos.dao.mapper.SonUserMapper;
import com.eaju.bos.dao.mapper.SysUserMapper;
import com.eaju.bos.entity.SysUser;
import com.eaju.bos.vo.ActiveUser;
import com.eaju.bos.vo.UserCustom;
/**
*
*/
public class ShiroUtil {
/**
*
* description: 获取ActiveUser并保存至session中一份
* @return
* @date 2016年8月15日 下午3:37:23
* @author MrDuan
*/
public static ActiveUser getActiveUser(){
//从shiro的session中取出activeUser
Subject subject = SecurityUtils.getSubject();
//取出身份信息
ActiveUser activeUser = (ActiveUser) subject.getPrincipal();
if(activeUser!=null){
Session session = subject.getSession();
ActiveUser user = (ActiveUser) session.getAttribute("user");
if(user==null){
session.setAttribute("user", activeUser);
}
return activeUser;
}else{
return null;
}
}
/**
* 根据sessionid 获取用户信息
* @param sessionID
* @param request
* @param response
* @return
*/
public static ActiveUser getActiveUser(String sessionID,HttpServletRequest request,HttpServletResponse response) throws Exception{
boolean status = false;
SessionKey key = new WebSessionKey(sessionID,request,response);
Session se = SecurityUtils.getSecurityManager().getSession(key);
Object obj = se.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
//org.apache.shiro.subject.SimplePrincipalCollection cannot be cast to com.hncxhd.bywl.entity.manual.UserInfo
SimplePrincipalCollection coll = (SimplePrincipalCollection) obj;
ActiveUser activeUser = (ActiveUser)coll.getPrimaryPrincipal();
if(activeUser!=null){
ActiveUser user = (ActiveUser) se.getAttribute("user");
if(user==null){
se.setAttribute("user", activeUser);
}
return activeUser;
}else{
return null;
}
}
}
sessionid 方式 Controller里获取用户信息
ActiveUser activeUser = null ;
try {
activeUser = ShiroUtil.getActiveUser(token, request, response);
} catch (Exception e1) {
if(UnknownSessionException.class.getName().equals(e1.getClass().getName())){
//抛出用户获取失败异常
retJsono.put("info", "用户获取失败!");
retJsono.put("returnCode",1000);
return retJsono;
}else{
retJsono.put("info", "内部错误!");
retJsono.put("returnCode",500);
return retJsono;
}
}
注意: 这里是举个例子,获取方式就是这个样子,ActiveUser 是你们自己存在shiro session里的用户实体,大家自己修改为自己的就可以了。
获取用户信息(token方式,每次请求过来都需要登录一下,不太推荐)
//登录获取用户信息
private ActiveUser loginToken(HttpServletRequest request) {
String token = request.getParameter("token");
Subject currentUser = SecurityUtils.getSubject();
byte[] decodeBase64 = Base64.decodeBase64(token);
String stringtoken = new String(decodeBase64);//Base64值
currentUser.login(new Gson().fromJson(stringtoken, UsernamePasswordToken.class));
ActiveUser activeUser = ShiroUtil.getActiveUser();
return activeUser;
}
为什么这么做?
因为前后端分离后,前后端可能会部署在不同的服务器上面,会跨域,前端每次请求时,都会是一次新的请求,所以前端需要记住一个用户的标识,每次请求数据都传给后端。后端才能知道是哪个用户。
以上均为个人想法,大家有好的意见或者建议,我会修改。