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

网友投稿 255 2023-01-29

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

导入依赖(pom.xml)

org.apache.shiro

shiro-spring

1.4.0

com.auth0

java-jwt

3.2.0

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

@Configuration

public class ShiroConfig {

/**

* ShiroFilterFactoryBean

*

* anon:无需认证就可以访问

* authc:必须认证才能访问

* user:必须拥有 记住我 功能才能用

* perms:拥有对某个资源的权限能访问

* role:拥有某个角色权限能访问

*/

@Bean

public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {

ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();

// 设置安全管理器

factoryBean.setSecurityManager(defaultWebSecurityManager);

// 添加shiro的内置过滤器

Map 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, UserEmailReawhhednELYolm userEmailRealm, AbstractAuthenticator abstractAuthenticator) {

DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();

List 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 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 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 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 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 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 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

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Spring Boot和Thymeleaf整合结合JPA实现分页效果(实例代码)
下一篇:今日头条免费api(今日头条免费安装)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~