软件编程
位置:首页>> 软件编程>> java编程>> SpringBoot 整合 Shiro 密码登录与邮件验证码登录功能(多 Realm 认证)

SpringBoot 整合 Shiro 密码登录与邮件验证码登录功能(多 Realm 认证)

作者:风青宇  发布时间:2023-06-23 02:45:28 

标签:SpringBoot,Shiro,登录

导入依赖(pom.xml)


<!--整合Shiro安全框架-->
 <dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-spring</artifactId>
  <version>1.4.0</version>
 </dependency>
 <!--集成jwt实现token认证-->
 <dependency>
  <groupId>com.auth0</groupId>
  <artifactId>java-jwt</artifactId>
  <version>3.2.0</version>
 </dependency>

在 SpringBoot 项目配置 config 包下创建 ShiroConfig 配置类


@Configuration
public class ShiroConfig {

/**
 * ShiroFilterFactoryBean
 * <p>
 * anon:无需认证就可以访问
 * authc:必须认证才能访问
 * user:必须拥有 记住我 功能才能用
 * perms:拥有对某个资源的权限能访问
 * role:拥有某个角色权限能访问
 */
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
 ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
 // 设置安全管理器
 factoryBean.setSecurityManager(defaultWebSecurityManager);
 // 添加shiro的内置过滤器
 Map<String, String> filterMap = new LinkedHashMap<>();
 // 放行不需要权限认证的接口
 // 网站首页
 filterMap.put("/", "anon");
 filterMap.put("/index", "anon");
 filterMap.put("/index.html", "anon");
 // 不验证跳转接口
 filterMap.put("/into/**", "anon");

// 需要权限认证的接口
 // 验证跳转接口
 filterMap.put("/verifyInto/**", "authc");

factoryBean.setFilterChainDefinitionMap(filterMap);

// 访问没有授权的资源
 factoryBean.setLoginUrl("redirect:/into/login");
 // 设置无权限时跳转的url
 factoryBean.setUnauthorizedUrl("redirect:/into/login");

return factoryBean;
}

/**
 * 管理shiro的生命周期
 */
@Bean("lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
 return new LifecycleBeanPostProcessor();
}

/**
 * 注入 密码登录CustomRealm
 */
@Bean
@DependsOn("lifecycleBeanPostProcessor")
public UserPasswordRealm userPasswordRealm() {
 return new UserPasswordRealm();
}

/**
 * 注入 邮箱验证登录EmailRealm
 */
@Bean
@DependsOn("lifecycleBeanPostProcessor")
public UserEmailRealm userEmailRealm() {
 return new UserEmailRealm();
}

/**
 * 默认安全管理器
 */
@Bean
public DefaultWebSecurityManager securityManager(UserPasswordRealm userPasswordRealm, UserEmailRealm userEmailRealm, AbstractAuthenticator abstractAuthenticator) {
 DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
 List<Realm> realms = new ArrayList<>();
 realms.add(userPasswordRealm);
 realms.add(userEmailRealm);
 defaultWebSecurityManager.setRealms(realms);
 // 记住我
 defaultWebSecurityManager.setRememberMeManager(cookieRememberMeManager());
 defaultWebSecurityManager.setAuthenticator(abstractAuthenticator);
 return defaultWebSecurityManager;
}

/**
 * 认证器 把我们的自定义验证加入到认证器中
 */
@Bean
public AbstractAuthenticator abstractAuthenticator(UserPasswordRealm userPasswordRealm, UserEmailRealm userEmailRealm) {
 // 自定义模块化认证器,用于解决多realm抛出异常问题
 //开始没用自定义异常问题,发现不管是账号密码错误还是什么错误
 //shiro只会抛出一个AuthenticationException异常
 ModularRealmAuthenticator authenticator = new MyCustomModularRealmAuthenticator();
 // 认证策略:AtLeastOneSuccessfulStrategy(默认),AllSuccessfulStrategy,FirstSuccessfulStrategy
 authenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
 // 加入realms
 List<Realm> realms = new ArrayList<>();
 realms.add(userPasswordRealm);
 realms.add(userEmailRealm);
 authenticator.setRealms(realms);
 return authenticator;
}

/**
 * 加入shiro注解 代理生成器 切面
 */
@Bean
@DependsOn({"lifecycleBeanPostProcessor"})
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
 DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
 advisorAutoProxyCreator.setProxyTargetClass(true);
 return advisorAutoProxyCreator;
}

/**
 * 加入shiro注解 切点
 */
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
 AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
 authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
 return authorizationAttributeSourceAdvisor;
}

/**
 * 设置cookie 记住我生成cookie
 */
@Bean
public CookieRememberMeManager cookieRememberMeManager() {
 CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
 cookieRememberMeManager.setCookie(rememberMeCookie());
 return cookieRememberMeManager;
}

/**
 * 设置cookie有效时间
 */
@Bean
public SimpleCookie rememberMeCookie() {
 /*这个参数是cookie的名称,对应前端页面的checkbox的name=remremberMe*/
 SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
 /*cookie的有效时间为30天,单位秒*/
 simpleCookie.setMaxAge(259200);
 return simpleCookie;
}

}

创建自定义验证器 MyCustomModularRealmAuthenticator 类


public class MyCustomModularRealmAuthenticator extends ModularRealmAuthenticator {

@Override
protected AuthenticationInfo doMultiRealmAuthentication(Collection<Realm> realms, AuthenticationToken token) {
 AuthenticationStrategy authenticationStrategy = this.getAuthenticationStrategy();
 AuthenticationInfo authenticationInfo = authenticationStrategy.beforeAllAttempts(realms, token);

Iterator var5 = realms.iterator();
 while (var5.hasNext()) {
  Realm realm = (Realm) var5.next();
  authenticationInfo = authenticationStrategy.beforeAttempt(realm, token, authenticationInfo);
  if (realm.supports(token)) {

AuthenticationInfo info = null;
   Throwable t = null;

info = realm.getAuthenticationInfo(token);

authenticationInfo = authenticationStrategy.afterAttempt(realm, token, info, authenticationInfo, t);
  }
 }
 authenticationInfo = authenticationStrategy.afterAllAttempts(token, authenticationInfo);
 return authenticationInfo;
}
}

创建密码登录时验证授权 UserPasswordRealm 类


@Component
public class UserPasswordRealm extends AuthorizingRealm {

// 注入用户业务
@Autowired
private UserMapper userMapper;

/**
 * 授权
 */
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
 System.out.println("————密码授权————doGetAuthorizationInfo————");

return null;
}

/**
 * 认证
 */
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
 System.out.println("————密码认证————doGetAuthenticationInfo————");

UsernamePasswordToken userToken = (UsernamePasswordToken) token;
 // 连接数据库 查询用户数据
 QueryWrapper<User> wrapper = new QueryWrapper<>();
 wrapper.eq("user_name", userToken.getUsername());
 User user = userMapper.selectOne(wrapper);
 // 验证用户
 if (user == null) {
  throw new UnknownAccountException();
 }
 return new SimpleAuthenticationInfo("", user.getUserPassword(), "");
}

/**
 * 用来判断是否使用当前的 realm
 *
 * @param var1 传入的token
 * @return true就使用,false就不使用
 */
@Override
public boolean supports(AuthenticationToken var1) {
 return var1 instanceof UsernamePasswordToken;
}

}

创建邮件验证码登录时验证授权UserEmailRealm


@Component
public class UserEmailRealm extends AuthorizingRealm {

// 注入用户业务
@Autowired
UserService userService;

@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
 System.out.println("————邮箱登录授权————doGetAuthorizationInfo————");
 return null;
}

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
 System.out.println("————邮箱登录认证————doGetAuthenticationInfo————");
 UserEmailToken userEmailToken = (UserEmailToken) token;
 String userEmail = (String) userEmailToken.getPrincipal();
 // 连接数据库 查询用户数据
 QueryWrapper<User> wrapper = new QueryWrapper<>();
 wrapper.eq("user_email", userEmail);
 User user = userService.getOne(wrapper);
 //因为没有密码,并且验证码在之前就验证了
 if (user == null) {
  throw new UnknownAccountException();
 }
 return new SimpleAuthenticationInfo("", userEmail, "");
}

/**
 * 用来判断是否使用当前的 realm
 *
 * @param var1 传入的token
 * @return true就使用,false就不使用
 */
@Override
public boolean supports(AuthenticationToken var1) {
 return var1 instanceof UserEmailToken;
}
}

创建邮件验证码登录验证通过生成令牌的 UserEmailToken 类(密码登录时使用shiro默认的 UsernamePasswordToken 令牌)


@Data // 使用lombok 生成get方法、set方法
public class UserEmailToken implements HostAuthenticationToken, RememberMeAuthenticationToken {

private String userEmail;
private boolean rememberMe;
private String host;

public UserEmailToken() {
 this.rememberMe = false;
}

public UserEmailToken(String userEmail) {
 this(userEmail, false, null);
}

public UserEmailToken(String userEmail, boolean rememberMe) {
 this(userEmail, rememberMe, null);
}

public UserEmailToken(String userEmail, boolean rememberMe, String host) {
 this.userEmail = userEmail;
 this.rememberMe = rememberMe;
 this.host = host;
}

@Override
public String getHost() {
 return host;
}

@Override
public boolean isRememberMe() {
 return rememberMe;
}

/**
 * 重写getPrincipal方法
 */
@Override
public Object getPrincipal() {
 return userEmail;
}

/**
 * 重写getCredentials方法
 */
@Override
public Object getCredentials() {
 return userEmail;
}
}

创建密码盐值加密 MDPasswordUtil 工具类


public class MDPasswordUtil {

public String getMDPasswordUtil(String userName, String userPassword) {
 String hashAlgorithmName = "MD5"; // 加密方式:md5加密
 Object credentials = userPassword; // 密码
 Object salt = ByteSource.Util.bytes(userName); // 盐
 int hashIterations = 512; // 加密次数
 Object result = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
 return result.toString();
}
}

控制层用户密码登录


// 用户密码登录
@PostMapping("/passwordLogin")
public String userLogin(@RequestParam("userName") String userName,
      @RequestParam("userPassword") String userPassword,
      HttpSession session, Model model) {
 // 获取当前的用户
 Subject subject = SecurityUtils.getSubject();
 // 对密码进行MD5盐值加密
 String md5Password = new MDPasswordUtil().getMDPasswordUtil(userName, userPassword);
 // 封装用户的登录数据
 UsernamePasswordToken token = new UsernamePasswordToken(userName, md5Password);
 //rememberme记住我
 token.setRememberMe(true);
 try {
  // 登录,验证,保存令牌
  subject.login(token);

//查询登录信息
  QueryWrapper<User> wrapper = new QueryWrapper<>();
  wrapper.eq("user_name", userName);
  User user = userService.getOne(wrapper);
  //保存登录用户信息
  session.setAttribute(user.getUserId().toString(), user);

return "admin";
 } catch (UnknownAccountException e) {
  model.addAttribute("userError", "用户名错误!请重新输入。");
  return "login";
 } catch (IncorrectCredentialsException ice) {
  model.addAttribute("pwError", "密码错误!请重新输入。");
  return "login";
 }
}

控制层用户邮件验证码密码登录


// 用户邮箱登录
@PostMapping("/emailLogin")
public String emailLogin(@RequestParam("userEmail") String userEmail,
       @RequestParam("emailCode") String emailCode,
       HttpSession session, Model model) {
 // 根据userEmail从session中取出发送的验证码
 String sendEmailCode = (String) session.getAttribute(userEmail);
 // 比对验证码
 if (StringUtils.isNoneBlank(sendEmailCode) && sendEmailCode.equals(emailCode)) {
  try {
   UserEmailToken token = new UserEmailToken(userEmail);
   //rememberme记住我
   token.setRememberMe(true);
   // 登录,验证,保存令牌
   Subject subject = SecurityUtils.getSubject();
   subject.login(token);

//查询登录信息
   QueryWrapper<User> wrapper = new QueryWrapper<>();
   wrapper.eq("user_email", userEmail);
   User user = userService.getOne(wrapper);
   //保存登录用户信息
   session.setAttribute(user.getUserId().toString(), user);

// 销毁验证码
   session.removeAttribute(emailCode);

return "admin";
  } catch (Exception e) {
   model.addAttribute("error", "验证码错误!请重新输入。");
   return "login";
  }
 } else {
  return "login";
 }
}

SpringBoot 整合 Shiro 密码登录与邮件验证码登录(多 Realm 认证)就可以了 (有点多,哈哈哈)

推荐大神:狂神说Java

来源:http://www.cnblogs.com/dmflysky/p/14449727.html

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com