• 导入依赖(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

SpringBoot 整合 Shiro 密码登录与邮件验证码登录(多 Realm 认证)的更多相关文章

  1. SpringBoot整合Shiro实现权限控制,验证码

    本文介绍 SpringBoot 整合 shiro,相对于 Spring Security 而言,shiro 更加简单,没有那么复杂. 目前我的需求是一个博客系统,有用户和管理员两种角色.一个用户可能有 ...

  2. SpringBoot整合Shiro完成验证码校验

    SpringBoot整合Shiro完成验证码校验 上一篇:SpringBoot整合Shiro使用Redis作为缓存 首先编写生成验证码的工具类 package club.qy.datao.utils; ...

  3. 补习系列(6)- springboot 整合 shiro 一指禅

    目标 了解ApacheShiro是什么,能做什么: 通过QuickStart 代码领会 Shiro的关键概念: 能基于SpringBoot 整合Shiro 实现URL安全访问: 掌握基于注解的方法,以 ...

  4. SpringBoot系列十二:SpringBoot整合 Shiro

    声明:本文来源于MLDN培训视频的课堂笔记,写在这里只是为了方便查阅. 1.概念:SpringBoot 整合 Shiro 2.具体内容 Shiro 是现在最为流行的权限认证开发框架,与它起名的只有最初 ...

  5. SpringBoot整合Shiro实现基于角色的权限访问控制(RBAC)系统简单设计从零搭建

    SpringBoot整合Shiro实现基于角色的权限访问控制(RBAC)系统简单设计从零搭建 技术栈 : SpringBoot + shiro + jpa + freemark ,因为篇幅原因,这里只 ...

  6. springboot整合Shiro功能案例

    Shiro 核心功能案例讲解 基于SpringBoot 有源码 从实战中学习Shiro的用法.本章使用SpringBoot快速搭建项目.整合SiteMesh框架布局页面.整合Shiro框架实现用身份认 ...

  7. SpringBoot 整合Shiro 一指禅

    目标 了解ApacheShiro是什么,能做什么: 通过QuickStart 代码领会 Shiro的关键概念: 能基于SpringBoot 整合Shiro 实现URL安全访问: 掌握基于注解的方法,以 ...

  8. SpringBoot整合Shiro+MD5+Salt+Redis实现认证和动态权限管理(上)----筑基中期

    写在前面 通过前几篇文章的学习,我们从大体上了解了shiro关于认证和授权方面的应用.在接下来的文章当中,我将通过一个demo,带领大家搭建一个SpringBoot整合Shiro的一个项目开发脚手架, ...

  9. SpringBoot整合Shiro+MD5+Salt+Redis实现认证和动态权限管理|前后端分离(下)----筑基后期

    写在前面 在上一篇文章<SpringBoot整合Shiro+MD5+Salt+Redis实现认证和动态权限管理(上)----筑基中期>当中,我们初步实现了SpringBoot整合Shiro ...

随机推荐

  1. XV6学习(10)锁

    在包括XV6的绝大部分操作系统都是多个任务交错执行的.交错的一个原因是多核硬件:多核计算机的多个CPU核心独立执行计算,如XV6的RISC-V处理器.多个CPU核心共享物理内存,XV6利用这种共享来维 ...

  2. 各个复位标志解析,让我们对MCU的程序的健康更有把控

    作者:良知犹存 转载授权以及围观:欢迎添加微信公众号:Conscience_Remains 总述 曾经开发的时候遇到这样情况,我们开发的设备需要长时间工作上报信息,但是我们在后台查看上报数据,发现设备 ...

  3. [NC13331]城市网络

    传送门 题意: 思路: 对于每组查询,我们直接从$u$往上搜到$v$,复杂度$O(nq)$,显然不可取(不过这题开始的数据很弱,暴力就过了) #include<bits/stdc++.h> ...

  4. hdu 4315 Climbing the Hill && poj 1704 Georgia and Bob阶梯博弈--尼姆博弈

    参考博客 先讲一下Georgia and Bob: 题意: 给你一排球的位置(全部在x轴上操作),你要把他们都移动到0位置,每次至少走一步且不能超过他前面(下标小)的那个球,谁不能操作谁就输了 题解: ...

  5. hdu2126 Buy the souvenirs

    Problem Description When the winter holiday comes, a lot of people will have a trip. Generally, ther ...

  6. 洛谷-P1434 [SHOI2002]滑雪 (记忆化搜索)

    题意:有一个\(R*C\)的矩阵,可以从矩阵中的任意一个数开始,每次都可以向上下左右选一个比当前位置小的数走,求走到\(1\)的最长路径长度. 题解:这题很明显看到就知道是dfs,但是直接爆搜会TLE ...

  7. 煎蛋网爬虫之JS逆向解析img路径

    图片使用js onload事件加载 <p><img src="//img.jandan.net/img/blank.gif" onload="janda ...

  8. 敏捷史话(六):也许这个人能拯救你的代码 —— Robert C. Martin

    Robert C. Martin( 罗伯特·C·马丁),作为世界级软件开发大师.设计模式和敏捷开发先驱.C++ Report杂志前主编,也是敏捷联盟(Agile Alliance)的第一任主席,我们尊 ...

  9. Chapter Zero 0.2.3 显示适配器

    显示适配器(Video Graphics Array,VGA) 不看后悔!!深入了解显卡!!!走你! 我们常常会调试显示器的分辨率,一般对于图像的显示重点在于分辨率与颜色深度, 每个图像显示的颜色会占 ...

  10. 大数据开发-从cogroup的实现来看join是宽依赖还是窄依赖

    前面一篇文章提到大数据开发-Spark Join原理详解,本文从源码角度来看cogroup 的join实现 1.分析下面的代码 import org.apache.spark.rdd.RDD impo ...