高级进阶使用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年的视频,希望大家工作中可以互相帮助,都是为了生活打拼,给自己一个机会、一点时间,工资翻一倍,生活还会像现在这么苦吗?

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

Shiro的remember me功能可以在用户登录后,保存用户的身份信息到cookie,这样当用户再次访问网站时,就可以通过cookie自动登录,而无需再次输入用户名和密码。下面是使用Shiroremember me功能的Java代码示例: 1. 配置Shiro的remember me功能 在Shiro配置文件,需要配置remember me功能,如下所示: ``` <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="myRealm"/> <property name="rememberMeManager" ref="rememberMeManager"/> </bean> <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager"> <property name="cookie" ref="rememberMeCookie"/> </bean> <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie"> <constructor-arg value="rememberMe"/> <property name="httpOnly" value="true"/> <property name="maxAge" value="2592000"/> <!-- 30 days --> </bean> ``` 这里配置了CookieRememberMeManager来管理remember me功能,同时指定了cookie的名称为“rememberMe”,并设置cookie的httpOnly属性为true,表示只能通过HTTP访问该cookie。另外,设置了cookie的最大有效期为30天。 2. 在登录时记住用户身份信息 在用户登录成功后,可以通过remember me功能将用户的身份信息保存到cookie,如下所示: ``` Subject currentUser = SecurityUtils.getSubject(); if (currentUser.isRemembered()) { // 如果用户已经通过remember me功能自动登录,则无需再次验证 } else { // 如果用户没有通过remember me功能自动登录,则需要验证用户名和密码 UsernamePasswordToken token = new UsernamePasswordToken(username, password); // 设置remember me功能 token.setRememberMe(true); currentUser.login(token); } ``` 在这里,如果用户已经通过remember me功能自动登录,则无需再次验证用户名和密码;否则,需要通过UsernamePasswordToken来验证用户名和密码,并设置remember me功能为true。最后,调用currentUser.login(token)方法来进行登录。 3. 在访问时自动登录 当用户再次访问网站时,可以通过cookie来自动登录,如下所示: ``` Subject currentUser = SecurityUtils.getSubject(); if (currentUser.isAuthenticated()) { // 如果用户已经通过验证,则直接访问 } else { // 如果用户没有通过验证,则尝试使用remember me功能自动登录 Cookie[] cookies = request.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { if (cookie.getName().equals("rememberMe")) { String rememberMe = cookie.getValue(); UsernamePasswordToken token = new UsernamePasswordToken("", "", true); token.setRememberMe(true); currentUser.login(token); break; } } } } ``` 在这里,如果用户已经通过验证,则直接访问;否则,尝试使用remember me功能自动登录。首先,获取所有的cookie,然后遍历所有的cookie,找到名称为“rememberMe”的cookie,获取cookie的值,通过UsernamePasswordToken来自动登录。最后,调用currentUser.login(token)方法来进行自动登录。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值