最近做了一个微信扫码登录的功能整合到若依中,当扫码完成后,如何确定是哪个用户,以及权限有哪些,因为shiro的加密基于MD5所以密码不可逆,则不能从数据库查询后再解密,简单一些的话可以把需要登录的用户账号密码写死在配置文件中,我之前就是这么干的,但是这样做的话一旦修改密码,则需要修改配置文件,所以我想到了免密登录,在网上查了好多shiro的改造,也没整明白怎么回事,继承的类也是不知道干嘛的,所以自己研究了一下若依的实现过程,终于实现了可免密登录
第一步:如果需要免密登录就需要有个标志来确定是不是从微信扫码过来的,不然账号密码登录就不用输入密码也能进来,这显然是不对的
创建一个枚举,来当做标志
package com.clpc.un.framework.shiro.wxconfig;
public enum LoginType {
PASSWORD("password"),
NOPASSWORD("nopassword");
private String code;
LoginType(String code) {
this.code = code;
}
public String getCode() {
return code;
}
}
第二步:我的业务是根据部门确定是哪个用户,写的比较low,还没做优化,在拿到部门id后就要进行登录,执行若依中原来的登录方法(改造过)
public void execLogin(HttpServletRequest request,String deptId){
log.info("登录用户部门Id:" + deptId);
String loginName;//登录用户名
if(deptId.equals("3") || deptId.equals("1190")){
loginName = "admin";
} else {
loginName = "ChinaLife";
}
//执行免密登录
loginController.ajaxLogin(request,loginName,"",true);
}
第三步:上一步的登录方法我是进行了改造,根据request获取访问过来的路径,由此判断是微信,还是账号密码,这里面的token也是改造过的,生成token时把登录类型带上,就是我们写的token
@PostMapping("/login")
@ResponseBody
public AjaxResult ajaxLogin(HttpServletRequest request,String username, String password, Boolean rememberMe)
{
//UsernamePasswordToken token = new UsernamePasswordToken(username, password, rememberMe);
//根据地址判断是否是微信登录
WxLoginToken token = null;
if(request.getServletPath().equals("/weChat/wechatLogin")){
token = new WxLoginToken(username, password, rememberMe, LoginType.NOPASSWORD);
} else {
token = new WxLoginToken(username, password, rememberMe, LoginType.PASSWORD);
}
Subject subject = SecurityUtils.getSubject();
try
{
subject.login(token);
return success();
}
catch (AuthenticationException e)
{
String msg = "用户或密码错误";
if (StringUtils.isNotEmpty(e.getMessage()))
{
msg = e.getMessage();
}
return error(msg);
}
}
第四部:修改UsernameAndPasswordToken,继承后修改即可,大部分都是继承过来的,只有一个登录方法,和变量type是自己写的,目的是在token中携带登录类型
package com.clpc.un.framework.shiro.wxconfig;
import org.apache.shiro.authc.UsernamePasswordToken;
public class WxLoginToken extends UsernamePasswordToken {
private LoginType type;
public WxLoginToken() {
super();
}
public WxLoginToken(String username, char[] password) {
super(username, password);
}
public WxLoginToken(String username, String password) {
super(username, password);
}
public WxLoginToken(String username, char[] password, String host) {
super(username, password, host);
}
public WxLoginToken(String username, String password, String host) {
super(username, password, host);
}
public WxLoginToken(String username, char[] password, boolean rememberMe) {
super(username, password, rememberMe);
}
public WxLoginToken(String username, String password, boolean rememberMe,LoginType type) {
super(username, password, rememberMe);
this.type = type;
}
public WxLoginToken(String username, char[] password, boolean rememberMe, String host) {
super(username, password, rememberMe, host);
}
public WxLoginToken(String username, String password, boolean rememberMe, String host) {
super(username, password, rememberMe);
}
@Override
public String getUsername() {
return super.getUsername();
}
@Override
public void setUsername(String username) {
super.setUsername(username);
}
@Override
public char[] getPassword() {
return super.getPassword();
}
@Override
public void setPassword(char[] password) {
super.setPassword(password);
}
@Override
public Object getPrincipal() {
return super.getPrincipal();
}
@Override
public Object getCredentials() {
return super.getCredentials();
}
@Override
public String getHost() {
return super.getHost();
}
@Override
public void setHost(String host) {
super.setHost(host);
}
@Override
public boolean isRememberMe() {
return super.isRememberMe();
}
@Override
public void setRememberMe(boolean rememberMe) {
super.setRememberMe(rememberMe);
}
@Override
public void clear() {
super.clear();
}
@Override
public String toString() {
return super.toString();
}
public LoginType getType() {
return type;
}
public void setType(LoginType type) {
this.type = type;
}
}
第五步:修改Realm,生成的token会传递给userRealm,所以这里面肯定是要修改的,把原来的token换成我们自己的token,并修改LoginService.login()方法
/**
* 登录认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException
{
//UsernamePasswordToken upToken = (UsernamePasswordToken) token;
//拿到token,此时token中存在登录类型,即是微信登录还是账号密码登录
WxLoginToken upToken = (WxLoginToken) token;
String username = upToken.getUsername();
String password = "";
if (upToken.getPassword() != null)
{
password = new String(upToken.getPassword());
}
User user = null;
try
{
user = wxLoginService.login(username, password,upToken.getType());
}
catch (CaptchaException e)
{
throw new AuthenticationException(e.getMessage(), e);
}
catch (UserNotExistsException e)
{
throw new UnknownAccountException(e.getMessage(), e);
}
catch (UserPasswordNotMatchException e)
{
throw new IncorrectCredentialsException(e.getMessage(), e);
}
catch (UserPasswordRetryLimitExceedException e)
{
throw new ExcessiveAttemptsException(e.getMessage(), e);
}
catch (UserBlockedException e)
{
throw new LockedAccountException(e.getMessage(), e);
}
catch (RoleBlockedException e)
{
throw new LockedAccountException(e.getMessage(), e);
}
catch (Exception e)
{
log.info("对用户[" + username + "]进行登录验证..验证未通过{}", e.getMessage());
throw new AuthenticationException(e.getMessage(), e);
}
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName());
return info;
}
第六步:修改LoginService,直接继承过来修改,尽量不改源文件,如果是通过账号密码登录则正常走原来的流程,验证密码之类的,如果来源于微信登录则直接去查数据库,不验证密码,直接返回查到的user
package com.clpc.un.framework.shiro.wxconfig;
import com.clpc.un.common.constant.Constants;
import com.clpc.un.common.exception.user.UserNotExistsException;
import com.clpc.un.common.utils.MessageUtils;
import com.clpc.un.framework.manager.AsyncManager;
import com.clpc.un.framework.manager.factory.AsyncFactory;
import com.clpc.un.framework.shiro.service.LoginService;
import com.clpc.un.project.system.user.domain.User;
import com.clpc.un.project.system.user.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class WxLoginService extends LoginService {
@Autowired
private IUserService userService;
@Override
public User login(String username, String password,LoginType type) {
if ( type == LoginType.PASSWORD ){
return super.login( username, password, type );
} else {
User user = userService.selectUserByLoginName(username);
if (user == null)
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.not.exists")));
throw new UserNotExistsException();
}
recordLoginInfo(user);
return user;
}
}
@Override
public void recordLoginInfo(User user) {
super.recordLoginInfo(user);
}
}
到此为止,免密登录就完成了,且不影响原有的密码登录,即使修改了密码也不影响我们微信登录,
此文作为自己的笔记,也给大家做个参考
文章手打不易,希望大家喜欢,多多支持,