Object's Blog

关于Shiro框架的学习(一)

字数统计: 4.5k阅读时长: 20 min
2019/08/18 分享

前言

由于最近在做一个项目,刚完成到登录注册,不想和以前的项目搬同样的砖了,想完成点不那么low的功能,像单点登录、权限控制等,于是就想起了Shiro框架。

初识Shiro

任何一种技术总有个开始,又总是这么巧,每个开始总是个HelloWorld。
官方给出的依赖:
shiro依赖

示例代码:

1
2
3
4
5
6
7
8
public class FirstShiro {
private static final transient Logger log = LoggerFactory.getLogger(FirstShiro.class);
public static void main(String[] args) {
// TODO Auto-generated method stub
log.info("My First Apache Shiro Application");
System.exit(0);
}
}

运行结果:

[main] INFO com.shiro.first.FirstShiro - My First Apache Shiro Application

Shiro概念

在没有Shiro的时候,我们在做项目中的登录、权限之类的功能有五花八门的实现方式,不同系统的做法不统一。但是有Shiro之后,大家就可以一致化地做权限系统,优点就是各自的代码不再晦涩难懂,有一套统一的标准。另外Shiro框架也比较成熟,能很好地满足需求。这就是我对Shiro的总结。

在Java SE中验证Shiro

Shiro不仅不依赖任何容器,可以在EE环境下运行,也可以在SE环境下运行,在快速入门中,我在SE环境下体验了Shiro的登录验证、角色验证、权限验证功能。

  • 配置文件方式

    • 在src目录下创建shiro.ini文件,内容如下:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      #定义各种用户
      [users]
      #用户 密码 角色

      #博客管理员
      Object=123456,BlogManager
      #读者
      Reader=654321,SimpleReader

      #定义各种角色
      [roles]
      #博客管理员权限
      BlogManager=addBlog,deleteBlog,modifyBlog,readBlog
      #普通读者权限
      SimpleReader=readBlog,commentBlog
    • 创建用户实体类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      /**
      * @author Object
      * 用户实体类
      */
      public class User {

      private String name;
      private String password;
      public String getName() {
      return name;
      }
      public void setName(String name) {
      this.name = name;
      }
      public String getPassword() {
      return password;
      }
      public void setPassword(String password) {
      this.password = password;
      }

      }
    • 创建ShiroTest,验证登录、权限、角色:

    获取当前用户

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
     /**
    * 获取当前用户(Subject)
    *
    * @param user
    * @return
    */
    public static Subject getSubject() {
    // 加载配置文件,获取SecurityManager工厂
    Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
    // 从工厂中获取SecurityManager对象
    SecurityManager securityManager = factory.getInstance();
    // 通过SecurityUtil将SecurityManager对象放入全局对象
    SecurityUtils.setSecurityManager(securityManager);
    // 全局对象通过SecurityManager生成Subject
    Subject subject = SecurityUtils.getSubject();
    return subject;
    }

    登录:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    /**
    * 用户登录方法
    *
    * @param user
    * @return
    */
    public static boolean login(User user) {
    Subject subject = getSubject();
    // 如果用户已经登录 则退出
    if (subject.isAuthenticated()) {
    subject.logout();
    }
    // 封装用户数据
    UsernamePasswordToken token = new UsernamePasswordToken(user.getName(), user.getPassword());
    // 验证用户数据
    try {
    subject.login(token);
    } catch (AuthenticationException e) {
    // 登录失败
    // e.printStackTrace();为了看结果,暂时不让它打印
    return false;
    }
    return subject.isAuthenticated();
    }

    判断用户是否为某个角色:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
       /**
    * 判断用户是否拥有某个角色
    *
    * @param user
    * @param role
    * @return
    */
    public static boolean hasRole(User user, String role) {
    Subject subject = getSubject();
    return subject.hasRole(role);
    }

    判断用户是否拥有某项权限

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
       /**
    * 判断用户是否拥有某种权限
    *
    * @param user
    * @param permit
    * @return
    */
    public static boolean isPermit(User user, String permit) {
    Subject subject = getSubject();
    return subject.isPermitted(permit);
    }

    有了这四个方法,我们就可以开始写测试类了。我会创建两个在配置文件中的用户 —— Object and Reader 和一个不在配置文件中的用户 —— Tom

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
     public static void main(String[] args) {
    // 用户Object
    User object = new User();
    object.setName("Object");
    object.setPassword("123456");

    // 用户Reader
    User reader = new User();
    reader.setName("Reader");
    // 错误的密码
    reader.setPassword("654321");

    // 不存在的用户
    User tom = new User();
    tom.setName("Tom");
    tom.setPassword("123456");

    List<User> users = new LinkedList<User>();
    users.add(object);
    users.add(reader);
    users.add(tom);

    // 角色:BlogManager
    String blogManager = "BlogManager";
    // 角色:SimpleReader
    String simpleReader = "SimpleReader";

    List<String> roles = new LinkedList<String>();
    roles.add(blogManager);
    roles.add(simpleReader);

    // 权限
    String addBlog = "addBlog";
    String deleteBlog = "deleteBlog";
    String modifyBlog = "modifyBlog";
    String readBlog = "readBlog";
    String commentBlog = "commentBlog";
    List<String> permits = new LinkedList<String>();
    permits.add(addBlog);
    permits.add(deleteBlog);
    permits.add(modifyBlog);
    permits.add(readBlog);
    permits.add(commentBlog);
    /**************************** 开始验证 ****************************/
    System.out.println("=========================验证用户是否登录成功=========================");
    // 验证用户是否登录成功
    for (User u : users) {
    if (login(u)) {
    System.out.println("用户:" + u.getName() + " 登录成功 " + "密码为:" + u.getPassword());
    } else {
    System.out.println("用户:" + u.getName() + " 登录失败 " + "密码为:" + u.getPassword());
    }
    }
    System.out.println("=========================验证用户角色信息=========================");
    // 验证用户角色
    for (User u : users) {
    for (String role : roles) {
    if (login(u)) {
    if (hasRole(u, role)) {
    System.out.println("用户:" + u.getName() + " 的角色是" + role);
    }
    }
    }
    }
    System.out.println("=========================验证用户权限信息=========================");
    for(User u:users) {
    System.out.println("========================="+u.getName()+"权限=========================");
    for(String permit:permits) {
    if(login(u)) {
    if(isPermit(u, permit)) {
    System.out.println("用户:"+u.getName() +" 有 "+permit+" 的权限 ");
    }
    }
    }
    }
    }

    运行结果如下(红字是由于缺少部分jar,暂不解决):
    运行结果

到这里为止,已经完成了Shiro的入门。但是在实际项目中,我们不可能用配置文件配置用户权限,所以还是得结合数据库进行开发。

Shiro结合数据库

  • RABC概念

    要结合数据库进行开发,得先理解一个概念 —— RABC

    RBAC 是当下权限系统的设计基础,同时有两种解释:
    一: Role-Based Access Control,基于角色的访问控制
    即:你要能够增删改查博客,那么当前用户就必须拥有博主这个角色。
    二:Resource-Based Access Control,基于资源的访问控制
    即,你要能够读博客、评论博客,那么当前用户就必须拥有读者这样的权限。

    所以,基于这个概念,我们的数据库将有:用户表、角色表、权限表、用户——角色关系表、权限——角色关系表,其中用户角色关系为多对多,即一个用户可以对应多个角色,一个角色也可以由多个用户扮演,权限角色关系也为多对多,即一个角色可以有多个权限,一个权限也可以赋予多个角色。

  • 需要的Jar包

    需要的jar

  • 数据库构建

    我使用的是MySQL,创建语句如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    CREATE DATABASE shiro;
    USE shiro;
    CREATE TABLE user(
    id bigint primary key auto_increment,
    name varchar(16),
    password varchar(32)
    )charset=utf8 ENGINE=InnoDB;

    create table role (
    id bigint primary key auto_increment,
    name varchar(32)
    ) charset=utf8 ENGINE=InnoDB;

    create table permission (
    id bigint primary key auto_increment,
    name varchar(32)
    ) charset=utf8 ENGINE=InnoDB;

    create table user_role (
    uid bigint,
    rid bigint,
    constraint pk_users_roles primary key(uid, rid)
    ) charset=utf8 ENGINE=InnoDB;

    create table role_permission (
    rid bigint,
    pid bigint,
    constraint pk_roles_permissions primary key(rid, pid)
    ) charset=utf8 ENGINE=InnoDB;

    往数据库中插入数据:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    INSERT INTO `user` VALUES (1,'Object','123456');
    INSERT INTO `user` VALUES (2,'Reader','654321');
    INSERT INTO `user_role` VALUES (1,1);
    INSERT INTO `user_role` VALUES (2,2);
    INSERT INTO `role` VALUES (1,'blogManager');
    INSERT INTO `role` VALUES (2,'reader');
    INSERT INTO `permission` VALUES (1,'addBlog');
    INSERT INTO `permission` VALUES (2,'deleteBlog');
    INSERT INTO `permission` VALUES (3,'modifyBlog');
    INSERT INTO `permission` VALUES (4,'readBlog');
    INSERT INTO `permission` VALUES (5,'commentBlog');
    INSERT INTO `role_permission` VALUES (1,1);
    INSERT INTO `role_permission` VALUES (1,2);
    INSERT INTO `role_permission` VALUES (1,3);
    INSERT INTO `role_permission` VALUES (1,4);
    INSERT INTO `role_permission` VALUES (2,4);
    INSERT INTO `role_permission` VALUES (2,5);
  • 工程构建

    • 创建实体类

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      public class User {
      private int id;
      private String name;
      private String password;

      public String getName() {
      return name;
      }
      public void setName(String name) {
      this.name = name;
      }
      public String getPassword() {
      return password;
      }
      public void setPassword(String password) {
      this.password = password;
      }
      public int getId() {
      return id;
      }
      public void setId(int id) {
      this.id = id;
      }
      }
    • 修改shiro.ini配置文件
      因为我们要从数据库中获取用户、角色、权限信息,所以就不需要在配置文件中配置了,但是我们要有一个Realm的概念,Realm在我的理解中,可以把它当作一个桥梁,当用户(Subject)请求Realm时,Realm就会去寻找ini配置文件或者数据库中的用户信息,Realm就是真正对用户做验证的关卡。我们要在DatabaseRealm中重写两个方法,分别是验证用户授权,那么Shiro怎么找到Realm呢,就是靠shiro.ini配置文件。

      1
      2
      3
      [main]
      databaseRealm=com.shirotest.DatabaseRealm
      securityManager.realms=$databaseRealm
    • 创建ShiroDao类
      这个类主要是从数据库中取出用户、权限列表、角色列表。
      代码如下:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      public class ShiroDao {
      private static Connection connection = null;
      private static PreparedStatement preparedStatement = null;
      static {
      try {
      Class.forName("com.mysql.jdbc.Driver");
      connection = DriverManager.getConnection(
      "jdbc:mysql://localhost:3306/shiro?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC",
      "root", "971103");
      } catch (ClassNotFoundException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
      } catch (SQLException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
      }
      }

      /**
      * 通过用户名获取密码
      *
      * @param username
      * @return
      */
      public static String getPassword(String username) {
      String sql = "select password from user where name = ?";
      ResultSet rs = null;
      try {
      preparedStatement = connection.prepareStatement(sql);
      preparedStatement.setString(1, username);
      rs = preparedStatement.executeQuery();
      if (rs.next())
      return rs.getString("password");
      } catch (SQLException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
      }
      return null;
      }

      public static Set<String> getRoles(String username) {
      String sql = "select role.name "
      + "from role,user_role,user "
      + "where user.id=user_role.uid "
      + "and user_role.rid=role.id "
      + "and user.name = ?";
      ResultSet rs = null;
      Set<String> set = new HashSet<>();
      try {
      preparedStatement = connection.prepareStatement(sql);
      preparedStatement.setString(1, username);
      rs = preparedStatement.executeQuery();
      while(rs.next()) {
      set.add(rs.getString("name"));
      }
      } catch (SQLException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
      }
      return set;
      }

      public static Set<String> getPermits(String username) {
      String sql = "select permission.name "
      + "from"
      + " permission,role_permission,role ,user_role,user "
      + "where "
      + "permission.id = role_permission.pid "
      + "and role_permission.rid = role.id "
      + "and role.id = user_role.rid "
      + "and user_role.uid = user.id "
      + "and user.name = ?";
      ResultSet rs = null;
      Set<String> set = new HashSet<>();
      try {
      preparedStatement = connection.prepareStatement(sql);
      preparedStatement.setString(1, username);
      rs = preparedStatement.executeQuery();
      while (rs.next()) {
      set.add(rs.getString("name"));
      }
      } catch (SQLException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
      }
      return set;
      }

      public static void main(String[] args) {
      System.out.println("Object的角色:" + new ShiroDao().getRoles("Object"));
      System.out.println("Reader的角色:" + new ShiroDao().getRoles("Reader"));
      System.out.println("Object的权限:"+new ShiroDao().getPermits("Object"));
      System.out.println("Reader的权限:"+new ShiroDao().getPermits("Reader"));
      }
      }

    运行结果:
    ShiroDao运行结果

    • DatabaseRealm

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      public class DatabaseRealm extends AuthorizingRealm{

      /**
      *授权的方法
      */
      @Override
      protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
      //只有认证成功了,Shiro才会调用这个方法进行授权
      //1.获取用户
      String username = (String) principal.getPrimaryPrincipal();
      //2.获取角色和权限列表
      Set<String> roles = ShiroDao.getRoles(username);
      Set<String> permissions = ShiroDao.getPermits(username);
      //3.授权
      SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
      simpleAuthorizationInfo.setRoles(roles);
      simpleAuthorizationInfo.setStringPermissions(permissions);

      return simpleAuthorizationInfo;
      }

      /**
      *验证用户名密码是否正确的方法
      */
      @Override
      protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
      //1.获取用户名密码
      UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
      //获取用户名
      String username = usernamePasswordToken.getUsername();
      //获取密码
      String password = usernamePasswordToken.getPassword().toString();
      //获取数据库中的密码
      String passwordInDatabase = ShiroDao.getPassword(username);
      //为空则表示没有当前用户,密码不匹配表示密码错误
      if(null == passwordInDatabase||!password.equals(passwordInDatabase)) {
      throw new AuthenticationException();
      }
      //认证信息:放用户名密码 getName()是父类的方法,返回当前类名
      SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username,password,getName());
      return simpleAuthenticationInfo;
      }
      }
    • 测试类TestShiro

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      99
      100
      101
      102
      103
      104
      105
      106
      107
      108
      109
      110
      111
      112
      113
      public class TestShiro {
      public static void main(String[] args) {
      // 用户Object
      User object = new User();
      object.setName("Object");
      object.setPassword("123456");

      // 用户Reader
      User reader = new User();
      reader.setName("Reader");
      // 错误的密码
      reader.setPassword("654321");

      // 不存在的用户
      User tom = new User();
      tom.setName("Tom");
      tom.setPassword("123456");

      List<User> users = new LinkedList<User>();
      users.add(object);
      users.add(reader);
      users.add(tom);

      // 角色:BlogManager
      String blogManager = "blogManager";
      // 角色:SimpleReader
      String simpleReader = "reader";

      List<String> roles = new LinkedList<String>();
      roles.add(blogManager);
      roles.add(simpleReader);

      // 权限
      String addBlog = "addBlog";
      String deleteBlog = "deleteBlog";
      String modifyBlog = "modifyBlog";
      String readBlog = "readBlog";
      String commentBlog = "commentBlog";
      List<String> permits = new LinkedList<String>();
      permits.add(addBlog);
      permits.add(deleteBlog);
      permits.add(modifyBlog);
      permits.add(readBlog);
      permits.add(commentBlog);
      /**************************** 开始验证 ****************************/
      System.out.println("=========================验证用户是否登录成功=========================");
      // 验证用户是否登录成功
      for (User u : users) {
      if (login(u)) {
      System.out.println("用户:" + u.getName() + " 登录成功 " + "密码为:" + u.getPassword());
      } else {
      System.out.println("用户:" + u.getName() + " 登录失败 " + "密码为:" + u.getPassword());
      }
      }
      System.out.println("=========================验证用户角色信息=========================");
      // 验证用户角色
      for (User u : users) {
      for (String role : roles) {
      if (login(u)) {
      if (hasRole(u, role)) {
      System.out.println("用户:" + u.getName() + " 的角色是" + role);
      }
      }
      }
      }
      System.out.println("=========================验证用户权限信息=========================");
      for(User u:users) {
      System.out.println("========================="+u.getName()+"权限=========================");
      for(String permit:permits) {
      if(login(u)) {
      if(isPermitted(u, permit)) {
      System.out.println("用户:"+u.getName() +" 有 "+permit+" 的权限 ");
      }
      }
      }
      }

      }
      public static Subject getSubject() {
      Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
      //获取安全管理者实例
      SecurityManager sm = factory.getInstance();
      //将安全管理者放入全局对象
      SecurityUtils.setSecurityManager(sm);
      //全局对象通过安全管理者生成Subject对象
      Subject subject = SecurityUtils.getSubject();
      return subject;
      }
      public static boolean login(User user) {
      Subject subject = getSubject();
      if(subject.isAuthenticated()) {
      //如果登录了,就退出登录
      subject.logout();
      }
      //封装用户数据
      AuthenticationToken token = new UsernamePasswordToken(user.getName(),user.getPassword());
      try {
      subject.login(token);
      }catch(AuthenticationException e) {
      return false;
      }
      return subject.isAuthenticated();
      }
      private static boolean hasRole(User user, String role) {
      Subject subject = getSubject();
      return subject.hasRole(role);
      }

      private static boolean isPermitted(User user, String permit) {
      Subject subject = getSubject();
      return subject.isPermitted(permit);
      }
      }

    最终测试结果:
    最终测试结果

Shiro加密

我们在没有Shiro的时候,也会使用各种加密算法来对用户的密码进行加密,Shiro框架也提供了自己的一套加密服务,这里就说说MD5+盐。

在不加盐的MD5中,虽然密码也是使用非对称算法加密,同样也不能回转为明文,但是别人可以使用穷举法列出最常用的密码,例如12345 它加密后永远都是同一个密文,一些别有用心的人就可以通过这种常见密文得知你的密码是12345。但是加盐就不一样,他是在你的密码原文的基础上添加上一个随机数,这个随机数也会随之保存在数据库中,但是黑客拿到你的密码之后他并不知道哪个随机数是多少,所以就很难再破译密码。

操作一番。
首先要在数据库中加一个”盐”字段
ALTER TABLE user add column salt varchar(100)
同时在User实体类中加一个salt

1
2
3
4
5
6
7
 private String salt;
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
this.salt = salt;
}

然后在ShiroDao中加一个注册用户的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public static boolean registerUser(String username,String password) {
/***********************************Shiro加密***********************************/
//获取盐值
String salt = new SecureRandomNumberGenerator().nextBytes().toString();
//加密次数
int times = 3;
//加密方式
String type = "md5";
//加密后的最终密码
String lastPassword = new SimpleHash(type, password, salt, times).toString();
/***********************************加密结束***********************************/

String sql = "INSERT INTO user(name,password,salt)VALUES(?,?,?)";
try {
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, username);
preparedStatement.setString(2, lastPassword);
preparedStatement.setString(3, salt);
if(preparedStatement.execute()) {
return true;
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
}
return false;
}

同时加一个获取用户的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static User getUser(String username) {
String sql = "select * from user where name = ?";
User user = new User();
try {
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, username);
ResultSet resultSet = preparedStatement.executeQuery();
while(resultSet.next()) {
user.setName(resultSet.getString("name"));
user.setPassword(resultSet.getString("password"));
user.setSalt(resultSet.getString("salt"));
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return user;
}

修改之前的DatabaseRealm类中的验证用户方法,加一个将用户输入的密码加密后与数据库中密码进行比对的逻辑。具体逻辑如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//1.获取用户名密码
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
//获取用户名
String username = usernamePasswordToken.getUsername();
//获取密码
String password = new String(usernamePasswordToken.getPassword());
System.out.println("明文密码:"+password);
//获取数据库中的用户
User user = ShiroDao.getUser(usernamePasswordToken.getUsername());
//String passwordInDatabase = ShiroDao.getPassword(username);


//将用户输入的密码做一个加密后与数据库中的进行比对
String passwordMd5 = new SimpleHash("md5", password, user.getSalt(), 3).toString();
System.out.println("salt:"+user.getSalt());
System.out.println("密文密码:"+passwordMd5);
System.out.println("正在验证中......");
//为空则表示没有当前用户,密码不匹配表示密码错误
if(null == user.getPassword()||!passwordMd5.equals(user.getPassword())) {
throw new AuthenticationException();
}
//认证信息:放用户名密码 getName9()是父类的方法,返回当前类名
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username,password,getName());
return simpleAuthenticationInfo;

main测试:

1
2
3
4
5
6
7
8
9
ShiroDao.registerUser("Object2", "321321");
User object2 = new User();
object2.setName("Object2");
object2.setPassword("321321");
if (login(object2)) {
System.out.println("登录成功");
} else {
System.out.println("登录失败");
}

最后结果:

加密结果

数据库结果:

数据库结果

第二种验证用户的方式

刚才我们是在doGetAuthenticationInfo方法中自己写了验证逻辑,再来捋一遍:
1.获取用户输入的密码
2.获取数据库中该用户的盐
3.将用户输入的密码进行加盐加密
4.将加密后的密码和数据库中的密码进行比对

大概是要经历这么多步骤吧。其实Shiro提供了一个HashedCredentialsMatcher ,可以自动帮我们做这些工作。

步骤:
1.修改配置文件

1
2
3
4
5
6
7
8
[main]
credentialsMatcher=org.apache.shiro.authc.credential.HashedCredentialsMatcher
credentialsMatcher.hashAlgorithmName=md5 #加密方式
credentialsMatcher.hashIterations=3 #刚才我们指定的加密次数
credentialsMatcher.storedCredentialsHexEncoded=true

databaseRealm=com.shirotest.DatabaseRealm
securityManager.realms=$databaseRealm

2.修改doGetAuthenticationInfo方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//1.获取用户名密码
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
//获取用户名
String username = usernamePasswordToken.getUsername();
//获取密码
String password = new String(usernamePasswordToken.getPassword());
System.out.println("明文密码:"+password);
//获取数据库中的用户
User user = ShiroDao.getUser(usernamePasswordToken.getUsername());
//String passwordInDatabase = ShiroDao.getPassword(username);


//将用户输入的密码做一个加密后与数据库中的进行比对
System.out.println("数据库中密码:"+user.getPassword());
String passwordMd5 = new SimpleHash("md5", password, user.getSalt(), 3).toString();
System.out.println("salt:"+user.getSalt());
System.out.println("密文密码:"+passwordMd5);
System.out.println("正在验证中......");
/*
* //为空则表示没有当前用户,密码不匹配表示密码错误 if(null ==
* user.getPassword()||!passwordMd5.equals(user.getPassword())) { throw new
* AuthenticationException(); }
*/
//认证信息:放用户名密码 getName9()是父类的方法,返回当前类名
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username,user.getPassword(),ByteSource.Util.bytes(user.getSalt()),getName());
return simpleAuthenticationInfo;

主要是修改了验证信息,将数据库中的密码和盐传入,让它自行判断,我们就无需再写判断逻辑了。SimpleAuthenticationInfo(username,user.getPassword(),ByteSource.Util.bytes(user.getSalt()),getName());

运行结果:
第二种方式运行结果

小结

到这里为止,Shiro关于SE的部分应该就告一段落了,之后要开始学习关于集成Web和集成框架了,我觉得对于Shiro的架构及原理,得单独浏览一遍,因为到此为止我也只知道Shiro是怎么使用的,但是其中Realm类中的那两个方法,何时调用,为什么会调用,还有SimpleAuthenticationInfo返回后是怎么判断登录成功或者失败的,可以说是很模糊,学完集成框架后我应该会选择再看看其中的原理。

欢迎大家访问我的个人博客:Object’s Blog

原文作者:Object

原文链接:http://blog.objectspace.cn/2019/08/18/关于Shiro框架的学习(一)/

发表日期:2019 August 18th, 5:00:01 pm

更新日期:2019 September 27th, 1:39:16 pm

版权声明:未经作者授权请勿转载

目录
  1. 1. 前言
  2. 2. 初识Shiro
  3. 3. Shiro概念
  4. 4. 在Java SE中验证Shiro
    1. 4.1. 配置文件方式
  5. 5. Shiro结合数据库
    1. 5.1. RABC概念
    2. 5.2. 需要的Jar包
    3. 5.3. 数据库构建
    4. 5.4. 工程构建
  6. 6. Shiro加密
  7. 7. 第二种验证用户的方式
  8. 8. 小结