spring boot+shiro实现多种登录方式

多种登录方式说白了就是账号密码登录和第三方登录(免密码)。账号密码登录的账号可能是手机号、邮箱、身份证号等等,这些到了shiro处理的时候就是username+password的处理;而第三方登录在本系统只需要校验用户是否存在。

所涉及的类

spring boot+shiro实现多种登录方式_第1张图片

开始

引入shiro maven


	org.apache.shiro
	shiro-spring
	${shiro.version}

配置基础的shiro

@Configuration
public class ShiroConfig {
	
	@Bean
	public ShiroRealm shiroRealm() {
		ShiroRealm shiroRealm = new ShiroRealm();
		shiroRealm.setCredentialsMatcher(new SimpleCredentialsMatcher());
		return shiroRealm;
	}

	@Bean
	public WebSecurityManager securityManager() {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		securityManager.setRealm(shiroRealm());
		return securityManager;
	}
	
	@Bean
	public ShiroFilterFactoryBean shiroFilter(WebSecurityManager securityManager) {
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		shiroFilterFactoryBean.setSecurityManager(securityManager);
		return shiroFilterFactoryBean;
	}
}

重写Token

 先写一个枚举,这个枚举包含登录的类型(账号密码登录?QQ登录?微信登录?)以及是否需要密码。以后系统新增一个登录方式,在这里写上即可

/**
 * 	登录类型
 */
public enum LoginType {
	
	NORMAL(1, true, "账号"),
	QQ(2, false, "QQ");
	
	private LoginType(int code, boolean requriedPassword, String name) {
		this.code = code;
		this.requriedPassword = requriedPassword;
		this.name = name;
	}
	
	private int code;//编码
	private boolean requriedPassword;//是否需要密码
	private String name;//名称

	public int getCode() {
		return code;
	}

	public void setCode(int code) {
		this.code = code;
	}

	public boolean isRequriedPassword() {
		return requriedPassword;
	}

	public void setRequriedPassword(boolean requriedPassword) {
		this.requriedPassword = requriedPassword;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

定义一个Token,重写UsernamePasswordToken

/**
 * 自定义登录令牌
 */
public class UserToken extends UsernamePasswordToken {

	private static final long serialVersionUID = 1L;

	/**
	 * 登录方式
	 */
	private LoginType loginType;

	public UserToken() {
		super();
	}

	public UserToken(String username, String password, LoginType loginType) {
		super(username, password);
		this.loginType = loginType;
	}

	/**
	 * 根据登录类型获取登录方式(这段可以先注释,因为还没有定义登录方式LoginWay)
	 * 
	 * @param loginType
	 * @return
	 */
	public LoginWay getLoginWay() {
        //获取所有的实现类
		Map loginWays = SpringContextUtils.getBeansOfType(LoginWay.class);
		for (LoginWay loginWay : loginWays.values()) {
			if (loginWay.loginType() == loginType) {//获取符合登录类型的登录方法
				loginWay.initByToken(this);//设置token到loginWay中
				return loginWay;
			}
		}
		return null;
	}

	public LoginType getLoginType() {
		return loginType;
	}

	public void setLoginType(LoginType loginType) {
		this.loginType = loginType;
	}
}

定义登录方式

先定义一个接口,以后增加登录方式时,需要继承这个接口、重写里面的方法

/**
 * 登录方式接口
 */
public interface LoginWay {

	/**
	 * 登录类型
	 * 
	 * @return
	 */
	LoginType loginType();

	/**
	 * 根据配对的令牌初始化登录方式
	 * 
	 * @param userToken
	 * @return
	 */
	void initByToken(UserToken userToken);

	/**
	 * 获取用户信息
	 * 
	 * @return
	 */
	LoginUserModel getUser();

	/**
	 * 是否免密
	 * 
	 * @return
	 */
	boolean requriedPassword();

	/**
	 * 验证密码
	 * 
	 * @param info
	 * @param password
	 * @return
	 */
	boolean isPasswordMatch();
}

先定义一个实现类,普通的账号密码登录,重写所有的方法。

/**
 * 账号密码登录方式
 */
@Component  //这里加@Component注解纯属是因为要调用查询数据库的LoginUserService...
public class NomalLogin implements LoginWay {

	@Autowired
	private LoginUserService loginUserService;

	private LoginType loginType = LoginType.NORMAL;//确定这是账号密码登录类型

	private UserToken token;//token作为这个类的成员变量,因为之后要用到token里面的账号及密码
    
    //第一个被userToken执行的方法,根据userToken中的登录类型判断是不是这个登录方式
	@Override
	public LoginType loginType() {
		return loginType;
	}
    
    //第二个被userToken执行的方法,初始化登录方式里面的token
	@Override
	public void initToken(UserToken token) {
		this.token = token;
	}

	@Override
	public boolean requriedPassword() {
		return loginType.isRequriedPassword();
	}

	@Override
	public LoginUserModel getUser() {
        //根据token的用户名数据库,判断用户是否存在
		return loginUserService.getUserByAccount(token.getUsername());
	}

	@Override
	public boolean isPasswordMatch() {
        //自定义加密方式,这取决于你在保存用户时采用的加密
        //其实就这个类需要加密,因为第三方登录都是免密的
		String password = MD5.md5(new String(token.getPassword()));
        //根据token的用户名及密码查询数据库,判断密码是否正确
		LoginUserModel user = loginUserService.getUserByNameAndPassword(token.getUsername(), password);
		return user != null;
	}
}

重写Realm

realm主要负责通过Token获取LoginWay,去校验账号和密码,通过抛出异常的方式将校验结果供controller捕捉

/**
 * 自定义realm
 */
public class ShiroRealm extends AuthorizingRealm {

	/**
	 * 认证
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
		UserToken userToken = (UserToken) token;
        //获得登录方式
		LoginWay loginWay = userToken.getLoginWay();
		LoginUserModel user = loginWay.getUser();
		if (user == null) {//用户不存在
			throw new AuthenticationException(ResponseTips.LOGINERROR_USERNOTEXIST);
		}
		if (loginWay.requriedPassword() && !loginWay.isPasswordMatch()) {//密码错误
			throw new AuthenticationException(ResponseTips.LOGINERROR_PASSWORDMISMATCH);
		}
		return new SimpleAuthenticationInfo(userToken.getUsername(), user.getPassword(), "ShiroReaml");
	}

	/**
	 * 授权
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		// TODO Auto-generated method stub
		return null;
	}

}

controller

controller要做的事情就是就是new一个UserToken

/**
	 * 登录验证
	 * @param user
	 * @param type
	 * @return
	 */
	private String login(LoginUserModel user, LoginType type) {
		try {
			UserToken token = new UserToken(user.getAccount(), user.getPassword(), type);
			Subject subject = SecurityUtils.getSubject();
			subject.login(token);
			return null;
		} catch (Exception e) {
			return e.getMessage();
		}
	}

这样,就可以做到基本的登录验证了。记住密码、会话域这些,shiro也有支持,可以自己完善

你可能感兴趣的:(java类)