服务安全与权限-shiro[02搭建]

417 阅读4分钟

这是我参与8月更文挑战的第9天,活动详情查看:8月更文挑战

整合springboot

前提

创建页面用于区分用户权限(A用户只能去add,不能去update)

1.创建新项目,导入web和thymeleaf

2.编写首页html,放在templates目录下!

3.编写add,update页面,放在templates/user目录下

4.编写controller跳转到首页,add,update

正式(模板)

1.导入shiro-spring整合包

<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->
<dependency>
   <groupId>org.apache.shiro</groupId>
   <artifactId>shiro-spring</artifactId>
   <version>1.5.3</version>
</dependency>

2.编写配置类(基本框架),

先编写realm的类(需要自定义)

public class realm extends AuthorizingRealm {
​
//   授权
   @Override
   protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
       System.out.println("realm认证----------------------AuthorizationInfo");
       return null;
  }
​
//   认证
   @Override
   protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throwsAuthenticationException {
       System.out.println("realm授权----------------------AuthenticationInfo");
       return null;
  }
}

shiroConfig顺序是先realm,再manager,再ShiroFilterFactoryBean

@Configuration
public class shiroConfig {
​
//   shiroFilterFactoryBan                       这是第三部,需要manage管理
   @Bean
   public ShiroFilterFactoryBean bean(@Qualifier("manager") DefaultWebSecurityManagerdefaultWebSecurityManager){
       ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//       设置安全管理器
       bean.setSecurityManager(defaultWebSecurityManager);
       return bean;
  }
​
//     DefaultWebSecurityManager               这是第二部,因为manager需要realm
   @Bean(name="manager")
   public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("realm") realmrealm){
       DefaultWebSecurityManager defaultWebSecurityManager = newDefaultWebSecurityManager();
//         关联realm   ,这里的参数不能直接realm(),因为realm是spring管理的,所以在方法的参数上用spring容器的bean对象
       defaultWebSecurityManager.setRealm(realm);
​
       return defaultWebSecurityManager;
  }
​
//   realm 对象,需要自定义(创建一个realm类)   这是第一步第一步
   @Bean
   public realm realm(){
       return new realm();
  }
}

3.具体实现用户权限

bean方法内

HashMap<String,String> map = new HashMap<>();
//       这里的资源写的是路径(controller里的),不是页面
       map.put("/toadd","authc");
       map.put("/toupdate","authc");
//         设置过滤器的内容(哪些路径要被过滤及其访问权限)
       bean.setFilterChainDefinitionMap(map);
​
//       设置跳转登录页面,当被拦截时跳转到登录页面
       bean.setLoginUrl("/tologin");

4.编写controller登录操作

@RequestMapping("/login")
   public String login(String username,String password,Model model){
       //       获取当前用户
       Subject subject = SecurityUtils.getSubject();
//       封装用户的登录数据
       UsernamePasswordToken token = new UsernamePasswordToken(username, password);
       try{
//     执行登录流程(所有验证的步骤shiro都帮我们做了),如果错误就会报异常
           subject.login(token);
           return "index";
      }catch (UnknownAccountException e){
           model.addAttribute("msg","用户不存在");
           return "/user/login";
      }catch (IncorrectCredentialsException e){
           model.addAttribute("msg","密码错误");
           return "/user/login";
      }
  }

登录:

image.png

然后查看控制台:发现执行了realm的方法

image.png

两者之间我们并没有做什么联系,但是shiro就帮我们自动连接起来了,那么就可以在realm中添加登录数据

5.realm编写数据获取和认证

//   认证
   @Override
   protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throwsAuthenticationException {
       System.out.println("realm认证----------------------AuthenticationInfo");
​
//     可以从数据库获取数据 ,这里手动设置数据
       String username="YY";
       String pasword = "1";
​
       UsernamePasswordToken usertoken = (UsernamePasswordToken)token;
//       账号认证 ,认证参数里的token是全局存在的,登陆那边封装好了,这边就可以用
       if(!usertoken.getUsername().equals(username)){
//           return null 即抛出异常,由于是判断username的,异常就是用户名不存在
           return null;
      }
//       我们不做密码认证,有可能泄漏   。shiro暗地里做密码认证,
       return new SimpleAuthenticationInfo("",pasword,"");
  }

登录时会智能判断异常情况

6.设置未授权页面

//       设置未授权请求的页面
       bean.setUnauthorizedUrl("/tounauthorized");

整合mybatis

前几部操作和springboot整合mybatis一样(可以多一步service层)

1.在realm的认证中添加数据库操作

//     从数据库获取数据
       User user = userService.findUserByUsername(usertoken.getUsername());
//       账号认证 ,认证参数里的token是全局存在的,登陆那边封装好了,这边就可以用
       if(user==null){
           return null;
      }
//       我们不做密码认证,有可能泄漏   。shiro暗地里做密码认证,
       return new SimpleAuthenticationInfo("",user.getPassword(),"");
  }

2.为当前用户添加权限

realme的授权方法(两种)

  • 为所有用户授予该权限
  • 通过认证中数据库操作获取user对象的权限并给予当前用户

注意:如果数据库没有区分大小写,那么输入大写也是会判定成功的 数据库字段从utf8_general_ci 改成 utf8_bin 可以了 或者增加一层判断 if(!user.getLoginname().equals(token.getUsername())){ throw new AuthenticationException(); }

//   授权,用于授权账号
   @Override
   protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//     只要经过这里就会授权
       SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//       为每个用户授予该权限
//       info.addStringPermission("user:add");//       获取当前登录的用户
       Subject subject = SecurityUtils.getSubject();
//       从下面的密码认证第一个 user拿到 user对象
       User user = (User)subject.getPrincipal();
//       为subject设置数据库user对象里的权限
       info.addStringPermission(user.getPerms());
​
       return info;
  }

用户登录情况

image.png

map.put("/toadd","perms[user:add]");
       map.put("/toupdate","perms[user:update]");

那么当YY登录时,只能访问update页面

当yzy登录时,不能访问任何网页

当y登录时,能访问add网页