SpringBoot integrates Shiro password login and mail verification code login functions of multi Realm authentication

  • 2021-08-31 07:58:46
  • OfStack

Import dependencies (pom. xml)


 <!-- Integration Shiro Security framework -->
  <dependency>
   <groupId>org.apache.shiro</groupId>
   <artifactId>shiro-spring</artifactId>
   <version>1.4.0</version>
  </dependency>
  <!-- Integration jwt Realization token Certification -->
  <dependency>
   <groupId>com.auth0</groupId>
   <artifactId>java-jwt</artifactId>
   <version>3.2.0</version>
  </dependency>

Create the ShiroConfig configuration class under the SpringBoot project configuration config package


@Configuration
public class ShiroConfig {

 /**
  * ShiroFilterFactoryBean
  * <p>
  * anon Access without authentication 
  * authc : Must be authenticated to access 
  * user : Must have   Remember me   Function can only be used 
  * perms Have permissions on a resource to access 
  * role Have a role permission to access 
  */
 @Bean
 public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
  ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
  //  Setting up Security Manager 
  factoryBean.setSecurityManager(defaultWebSecurityManager);
  //  Add shiro Built-in filter of 
  Map<String, String> filterMap = new LinkedHashMap<>();
  //  Release interfaces that do not require permission authentication 
  //  Home page of website 
  filterMap.put("/", "anon");
  filterMap.put("/index", "anon");
  filterMap.put("/index.html", "anon");
  //  Do not verify the jump interface 
  filterMap.put("/into/**", "anon");

  //  Interfaces that require permission authentication 
  //  Verify jump interface 
  filterMap.put("/verifyInto/**", "authc");
  
  factoryBean.setFilterChainDefinitionMap(filterMap);

  //  Access unauthorized resources 
  factoryBean.setLoginUrl("redirect:/into/login");
  //  Object that does not have time-limited jump url
  factoryBean.setUnauthorizedUrl("redirect:/into/login");

  return factoryBean;
 }

 /**
  *  Management shiro Life cycle of 
  */
 @Bean("lifecycleBeanPostProcessor")
 public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
  return new LifecycleBeanPostProcessor();
 }

 /**
  *  Injection   Password login CustomRealm
  */
 @Bean
 @DependsOn("lifecycleBeanPostProcessor")
 public UserPasswordRealm userPasswordRealm() {
  return new UserPasswordRealm();
 }

 /**
  *  Injection   Mailbox authentication login EmailRealm
  */
 @Bean
 @DependsOn("lifecycleBeanPostProcessor")
 public UserEmailRealm userEmailRealm() {
  return new UserEmailRealm();
 }

 /**
  *  Default Security Manager 
  */
 @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);
  //  Remember me 
  defaultWebSecurityManager.setRememberMeManager(cookieRememberMeManager());
  defaultWebSecurityManager.setAuthenticator(abstractAuthenticator);
  return defaultWebSecurityManager;
 }

 /**
  *  Authenticator   Add our custom authentication to the authenticator 
  */
 @Bean
 public AbstractAuthenticator abstractAuthenticator(UserPasswordRealm userPasswordRealm, UserEmailRealm userEmailRealm) {
  //  Custom modular authenticator for solving multiple realm Throw an exception question 
  // I didn't use custom exception at first, and found that no matter whether the account password is wrong or what is wrong 
  //shiro Will only throw 1 A AuthenticationException Anomaly 
  ModularRealmAuthenticator authenticator = new MyCustomModularRealmAuthenticator();
  //  Authentication policy: AtLeastOneSuccessfulStrategy( Default ) , AllSuccessfulStrategy , FirstSuccessfulStrategy
  authenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
  //  Join realms
  List<Realm> realms = new ArrayList<>();
  realms.add(userPasswordRealm);
  realms.add(userEmailRealm);
  authenticator.setRealms(realms);
  return authenticator;
 }

 /**
  *  Join shiro Annotation   Proxy generator   Sectional plane 
  */
 @Bean
 @DependsOn({"lifecycleBeanPostProcessor"})
 public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
  DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
  advisorAutoProxyCreator.setProxyTargetClass(true);
  return advisorAutoProxyCreator;
 }

 /**
  *  Join shiro Annotation   Tangent point 
  */
 @Bean
 public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
  AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
  authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
  return authorizationAttributeSourceAdvisor;
 }

 /**
  *  Settings cookie  Remember I generated cookie
  */
 @Bean
 public CookieRememberMeManager cookieRememberMeManager() {
  CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
  cookieRememberMeManager.setCookie(rememberMeCookie());
  return cookieRememberMeManager;
 }

 /**
  *  Settings cookie Effective time 
  */
 @Bean
 public SimpleCookie rememberMeCookie() {
  /* This parameter is cookie The name of the front-end page, corresponding to the checkbox Adj. name=remremberMe*/
  SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
  /*cookie The valid time of is 30 Days, unit seconds */
  simpleCookie.setMaxAge(259200);
  return simpleCookie;
 }

}

Create a custom validator MyCustomModularRealmAuthenticator class


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;
 }
}

Verify authorization UserPasswordRealm class when creating password login


@Component
public class UserPasswordRealm extends AuthorizingRealm {

 //  Inject user service 
 @Autowired
 private UserMapper userMapper;

 /**
  *  Authorization 
  */
 @Override
 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
  System.out.println(" Password Authorization doGetAuthorizationInfo --- ");

  return null;
 }

 /**
  *  Certification 
  */
 @Override
 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
  System.out.println(" Password authentication doGetAuthenticationInfo --- ");

  UsernamePasswordToken userToken = (UsernamePasswordToken) token;
  //  Connect to a database   Query user data 
  QueryWrapper<User> wrapper = new QueryWrapper<>();
  wrapper.eq("user_name", userToken.getUsername());
  User user = userMapper.selectOne(wrapper);
  //  Authenticate the user 
  if (user == null) {
   throw new UnknownAccountException();
  }
  return new SimpleAuthenticationInfo("", user.getUserPassword(), "");
 }

 /**
  *  Used to determine whether to use the current  realm
  *
  * @param var1  Incoming token
  * @return true Just use, false You don't use it 
  */
 @Override
 public boolean supports(AuthenticationToken var1) {
  return var1 instanceof UsernamePasswordToken;
 }

}

Create Mail Verification Code Login Verification Authorization UserEmailRealm Class


@Component
public class UserEmailRealm extends AuthorizingRealm {

 //  Inject user service 
 @Autowired
 UserService userService;

 @Override
 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
  System.out.println(" Mailbox login authorization doGetAuthorizationInfo --- ");
  return null;
 }

 @Override
 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
  System.out.println(" E-mail login authentication doGetAuthenticationInfo --- ");
  UserEmailToken userEmailToken = (UserEmailToken) token;
  String userEmail = (String) userEmailToken.getPrincipal();
  //  Connect to a database   Query user data 
  QueryWrapper<User> wrapper = new QueryWrapper<>();
  wrapper.eq("user_email", userEmail);
  User user = userService.getOne(wrapper);
  // Because there is no password and the verification code is verified before 
  if (user == null) {
   throw new UnknownAccountException();
  }
  return new SimpleAuthenticationInfo("", userEmail, "");
 }

 /**
  *  Used to determine whether to use the current  realm
  *
  * @param var1  Incoming token
  * @return true Just use, false You don't use it 
  */
 @Override
 public boolean supports(AuthenticationToken var1) {
  return var1 instanceof UserEmailToken;
 }
}

Creating a mail verification code login validates through the UserEmailToken class that generates the token (password login uses the shiro default UsernamePasswordToken token)


@Data //  Use lombok  Generate get Method, set Method 
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;
 }

 /**
  *  Rewrite getPrincipal Method 
  */
 @Override
 public Object getPrincipal() {
  return userEmail;
 }

 /**
  *  Rewrite getCredentials Method 
  */
 @Override
 public Object getCredentials() {
  return userEmail;
 }
}

Creating Cryptographic Salt Encryption MDPasswordUtil Tool Class


public class MDPasswordUtil {

 public String getMDPasswordUtil(String userName, String userPassword) {
  String hashAlgorithmName = "MD5"; //  Encryption method: md5 Encryption 
  Object credentials = userPassword; //  Password 
  Object salt = ByteSource.Util.bytes(userName); //  Salt 
  int hashIterations = 512; //  Encryption times 
  Object result = new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations);
  return result.toString();
 }
}

Control layer user password login


//  User password login 
 @PostMapping("/passwordLogin")
 public String userLogin(@RequestParam("userName") String userName,
       @RequestParam("userPassword") String userPassword,
       HttpSession session, Model model) {
  //  Get the current user 
  Subject subject = SecurityUtils.getSubject();
  //  For the password MD5 Salt value encryption 
  String md5Password = new MDPasswordUtil().getMDPasswordUtil(userName, userPassword);
  //  Encapsulate the user's login data 
  UsernamePasswordToken token = new UsernamePasswordToken(userName, md5Password);
  //rememberme Remember me 
  token.setRememberMe(true);
  try {
   //  Login, Authenticate, Save Token 
   subject.login(token);

   // Query login information 
   QueryWrapper<User> wrapper = new QueryWrapper<>();
   wrapper.eq("user_name", userName);
   User user = userService.getOne(wrapper);
   // Save login user information 
   session.setAttribute(user.getUserId().toString(), user);

   return "admin";
  } catch (UnknownAccountException e) {
   model.addAttribute("userError", " Wrong user name! Please re-enter. ");
   return "login";
  } catch (IncorrectCredentialsException ice) {
   model.addAttribute("pwError", " Wrong password! Please re-enter. ");
   return "login";
  }
 }

Control layer user mail verification code password login


//  User mailbox login 
 @PostMapping("/emailLogin")
 public String emailLogin(@RequestParam("userEmail") String userEmail,
        @RequestParam("emailCode") String emailCode,
        HttpSession session, Model model) {
  //  According to userEmail From session Take out the sent verification code from 
  String sendEmailCode = (String) session.getAttribute(userEmail);
  //  Comparison verification code 
  if (StringUtils.isNoneBlank(sendEmailCode) && sendEmailCode.equals(emailCode)) {
   try {
    UserEmailToken token = new UserEmailToken(userEmail);
    //rememberme Remember me 
    token.setRememberMe(true);
    //  Login, Authenticate, Save Token 
    Subject subject = SecurityUtils.getSubject();
    subject.login(token);

    // Query login information 
    QueryWrapper<User> wrapper = new QueryWrapper<>();
    wrapper.eq("user_email", userEmail);
    User user = userService.getOne(wrapper);
    // Save login user information 
    session.setAttribute(user.getUserId().toString(), user);

    //  Destroy verification code 
    session.removeAttribute(emailCode);

    return "admin";
   } catch (Exception e) {
    model.addAttribute("error", " Verification code error! Please re-enter. ");
    return "login";
   }
  } else {
   return "login";
  }
 }

SpringBoot integrates Shiro password login and email verification code login (more Realm authentication) (a little more, hahaha)

Recommended great god: crazy god says Java


Related articles: