高级进阶使用Shiro中Remember Me和多Realm配置使用

二、RememberMe

将用户对页面访问的权限分为三个级别:

  • 未认证—可访问的页面—(陌生人)—问候

    • login.html、regist.html

  • 记住我—可访问的页面—(前女友)—朋友间的拥抱

    • info.html

  • 已认证—可访问的页面—(现女友)—牵手

    • 转账.html

2.1 在过滤器中设置“记住我”可访问的url

// anon     表示未认证可访问的url
// user     表示记住我可访问的url(已认证也可以访问),例如某些页面在之前登陆过,安全级别又不高时,可以在用户再次浏览时呈现,这时可以设置为user
//authc     表示已认证可访问的url,对于某些安全级别很高的,即使几天或几小时之前登陆过,但是在再次进行操作时还必须得确认登陆或安全性时,比如付款时再次确认用户身份,这时可以设置为authc
//perms     表示必须具备指定的权限才可访问
//logout    表示指定退出的url
filterMap.put("/","anon");
// 为user表示"记住我",但是当用户需要一些其他的认证才能访问时,必须要进行验证才能访问
// user要比authc低一等级
filterMap.put("/index.html","user");
filterMap.put("/login.html","anon");
filterMap.put("/regist.html","anon");
filterMap.put("/user/login","anon");
filterMap.put("/user/regist","anon");
filterMap.put("/layui/**","anon");
filterMap.put("/**","authc");
filterMap.put("/c_add.html","perms[sys:c:save]");
filterMap.put("/exit","logout");

2.2 在ShiroConfig.java中配置基于cookie的rememberMe管理器

@Bean
public CookieRememberMeManager cookieRememberMeManager(){
    CookieRememberMeManager rememberMeManager = new CookieRememberMeManager();
   
    //cookie必须设置name
    SimpleCookie cookie = new SimpleCookie("rememberMe");
    cookie.setMaxAge(30*24*60*60);
    
    rememberMeManager.setCookie(cookie);
    return  rememberMeManager;
}
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(MyRealm myRealm){
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    securityManager.setRealm(myRealm);
    securityManager.setCacheManager(getEhCacheManager());
    securityManager.setSessionManager(getDefaultWebSessionManager());
    //设置remember管理器
    securityManager.setRememberMeManager(cookieRememberMeManager());
    return securityManager;
}

2.3 登录认证时设置token“记住我”

  • 登录页面

<form action="/user/login" method="post">
    <p>帐号:<input type="text" name="userName"/></p>
    <p>密码:<input type="text" name="userPwd"/></p>
    <p>记住我:<input type="checkbox" name="rememberMe"/></p>
    <p><input type="submit" value="登录"/></p>
</form>
  • 控制器

@Controller
@RequestMapping("user")
public class UserController {
​
    @Resource
    private UserServiceImpl userService;
​
    @RequestMapping("login")
    public String login(String userName,String userPwd,boolean rememberMe){
        try {
            userService.checkLogin(userName,userPwd,rememberMe);
            System.out.println("------登录成功!");
            return "index";
        } catch (Exception e) {
            System.out.println("------登录失败!");
            return "login";
        }
​
    }
    
    //...
}
  • service

@Service
public class UserServiceImpl {
​
    public void checkLogin(String userName, String userPwd,boolean rememberMe) throws Exception {
        //Shiro进行认证 ——入口
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(userName,userPwd);
        token.setRememberMe(rememberMe);
        subject.login(token);
    }
}

三、Shiro多Realm配置

3.1 使用场景

  • 当shiro进行权限管理,数据来自于不同的数据源时,我们可以给SecurityManager配置多个Realm

 

Shiro配置多个Realm

3.2 多个Realm的处理方式

3.2.1 链式处理

  • 多个Realm依次进行认证,只要有一个Realm认证成功就表示认证成功,但是每次都要对设置的所有Realm进行认证。

3.2.2 分支处理

  • 根据不同的条件从多个Realm中选择一个进行认证处理

3.3 多Realm配置(链式处理)

  • 定义多个Realm

    • UserRealm

      public class UserRealm extends AuthorizingRealm {
      ​
          Logger logger = LoggerFactory.getLogger(UserRealm.class);
      ​
          @Override
          public String getName() {
              return "UserRealm";
          }
      ​
          @Override
          protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
              return null;
          }
      ​
          @Override
          protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
              logger.info("--------------------------------UserRealm");
              //从token中获取username
              UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
              String username = token.getUsername();
              //根据username从users表中查询用户信息
      ​
              SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username,"123456",getName());
              return info;
          }
      }
    • ManagerRealm

      public class ManagerRealm extends AuthorizingRealm {
      ​
          Logger logger = LoggerFactory.getLogger(ManagerRealm.class);
      ​
          @Override
          public String getName() {
              return "ManagerRealm";
          }
      ​
          @Override
          protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
              return null;
          }
      ​
          @Override
          protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
              logger.info("--------------------------------ManagerRealm");
              //从token中获取username
              UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
              String username = token.getUsername();
              //根据username从吗managers表中查询用户信息
      ​
              SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username,"222222",getName());
              return info;
          }
      }
  • 在ShiroConfig.java中为SecurityManager配置多个Realm

    @Configuration
    public class ShiroConfig {
    ​
        @Bean
        public UserRealm userRealm(){
            UserRealm userRealm = new UserRealm();
            return  userRealm;
        }
    ​
        @Bean
        public ManagerRealm managerRealm(){
            ManagerRealm managerRealm = new ManagerRealm();
            return managerRealm;
        }
    ​
        @Bean
        public DefaultWebSecurityManager getDefaultWebSecurityManager(){
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            
            //securityManager中配置多个realm
            Collection<Realm> realms = new ArrayList<>();
            realms.add(userRealm());
            realms.add(managerRealm());
    ​
            securityManager.setRealms(realms);
            return securityManager;
        }
    ​
       //...
    ​
    }
    ​
  • 测试代码:

    • login.html

      <form action="user/login" method="post">
          <p>帐号:<input type="text" name="userName"/></p>
          <p>密码:<input type="text" name="userPwd"/></p>
          <p><input type="radio" name="loginType" value="User"/>普通用户
              <input type="radio" name="loginType" value="Manager"/>管理员</p>
      ​
          <p><input type="submit" value="登录"/></p>
      </form>
    • UserController.java

      @Controller
      @RequestMapping("user")
      public class UserController {
          Logger logger = LoggerFactory.getLogger(UserController.class);
      ​
          @RequestMapping("login")
          public String login(String userName,String userPwd, String loginType){
              logger.info("~~~~~~~~~~~~~UserController-login");
              try{
                  UsernamePasswordToken token = new UsernamePasswordToken(userName,userPwd);
                  Subject subject = SecurityUtils.getSubject();
                  subject.login(token);
                  return "index";
              }catch (Exception e){
                  return "login";
              }
      ​
          }
      ​
      }

3.4 Shiro认证处理源码分析

 
 
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
        this.assertRealmsConfigured();
        Collection<Realm> realms = this.getRealms();
        
        // this.doMultiRealmAuthentication(realms, authenticationToken);中的realms参数就是认证会执行的Realm
        return realms.size() == 1 ? this.doSingleRealmAuthentication((Realm)realms.iterator().next(), authenticationToken) : this.doMultiRealmAuthentication(realms, authenticationToken);
    }

3.5 多Realm配置(分支处理)

根据不同的条件执行不同的Realm

  • 流程分析

     

    流程分析

    流程分析

  • 实现案例:用户不同身份登录执行不同的Realm

    • 自定义Realm(UserRealm\ManagerRealm)

      • 当在登录页面选择“普通用户”登录,则执行UserRealm的认证

      • 当在登录页面选择“管理员”登录,则执行ManagerRealm的认证

    • Realm的声明及配置

    • 自定义Token

      public class MyToken extends UsernamePasswordToken {
      ​
          private String loginType;
      ​
          public MyToken(String userName,String userPwd, String loginType) {
              super(userName,userPwd);
              this.loginType = loginType;
          }
      ​
          public String getLoginType() {
              return loginType;
          }
      ​
          public void setLoginType(String loginType) {
              this.loginType = loginType;
          }
      }
    • 自定义认证器

      public class MyModularRealmAuthenticator extends ModularRealmAuthenticator {
      ​
          Logger logger = LoggerFactory.getLogger(MyModularRealmAuthenticator.class);
      ​
          @Override
          protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
              logger.info("------------------------------MyModularRealmAuthenticator");
      ​
              this.assertRealmsConfigured();
              Collection<Realm> realms = this.getRealms();
      ​
              MyToken token = (MyToken) authenticationToken;
              String loginType = token.getLoginType(); // User
              logger.info("------------------------------loginType:"+loginType);
      ​
              Collection<Realm> typeRealms = new ArrayList<>();
              for(Realm realm:realms){
                  if(realm.getName().startsWith(loginType)){  //UserRealm
                      typeRealms.add(realm);
                  }
              }
      ​
             if(typeRealms.size()==1){
                 return this.doSingleRealmAuthentication((Realm)typeRealms.iterator().next(), authenticationToken);
             }else{
                 return this.doMultiRealmAuthentication(typeRealms, authenticationToken);
             }
      ​
          }
      ​
      }
    • 配置自定义认证器

      @Configuration
      public class ShiroConfig {
      ​
          @Bean
          public UserRealm userRealm(){
              UserRealm userRealm = new UserRealm();
              return  userRealm;
          }
      ​
          @Bean
          public ManagerRealm managerRealm(){
              ManagerRealm managerRealm = new ManagerRealm();
              return managerRealm;
          }
      ​
          @Bean
          public MyModularRealmAuthenticator myModularRealmAuthenticator(){
              MyModularRealmAuthenticator myModularRealmAuthenticator = new MyModularRealmAuthenticator();
              return myModularRealmAuthenticator;
          }
      ​
          @Bean
          public DefaultWebSecurityManager getDefaultWebSecurityManager(){
              DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
              //配置自定义认证器(放在realms设置之前)
              securityManager.setAuthenticator(myModularRealmAuthenticator());
      ​
              //securityManager中配置多个realm
              Collection<Realm> realms = new ArrayList<>();
              realms.add(userRealm());
              realms.add(managerRealm());
      ​
              securityManager.setRealms(realms);
              return securityManager;
          }
      ​
          //...
      ​
      }
    • 测试:控制器接受数据进行认证

      • login.html

      <form action="user/login" method="post">
          <p>帐号:<input type="text" name="userName"/></p>
          <p>密码:<input type="text" name="userPwd"/></p>
          <p><input type="radio" name="loginType" value="User" checked/>普通用户
          <input type="radio" name="loginType" value="Manager"/>管理员</p>
      ​
          <p><input type="submit" value="登录"/></p>
      </form>
      • UserController.java

      @Controller
      @RequestMapping("user")
      public class UserController {
          Logger logger = LoggerFactory.getLogger(UserController.class);
      ​
          @RequestMapping("login")
          public String login(String userName,String userPwd, String loginType){
              logger.info("~~~~~~~~~~~~~UserController-login");
              try{
                  //UsernamePasswordToken token = new UsernamePasswordToken(userName,userPwd);
                  MyToken token = new MyToken(userName,userPwd,loginType);
                  Subject subject = SecurityUtils.getSubject();
                  subject.login(token);
                  return "index";
              }catch (Exception e){
                  return "login";
              }
      ​
          }
      ​
      }

四、单体项目开发技术清单

4.1 JSP应用

  • View JSP(Java Server Page)

  • Control Servlet

  • Model JDBC(Java Database Connection)

  • 第一阶段的项目:JSP/Servlet+JDBC

4.2 SSM

  • View JSP

  • Control SpringMVC

  • Model MyBatis

  • 第二阶段项目:JSP+SSM

4.3 SpringBoot

  • View Thymeleaf

  • Control SpringMVC

  • Model MyBatis/tkMapper

  • 第三阶段练习项目:thymeleaf+SpringBoot(SSM)

单体项目:项目的前端页面与服务端的代码在同一个项目中(部署在同一个服务器上)

 

分析

技术交流或者学习资源获取可加VX:

对于开发技术的技术语言学习,例如java、android、python等总结了一系列2020年的视频,希望大家工作中可以互相帮助,都是为了生活打拼,给自己一个机会、一点时间,工资翻一倍,生活还会像现在这么苦吗?

å¨è¿éæå¥å¾çæè¿°

猜你喜欢

转载自blog.csdn.net/u014748504/article/details/107895406