• Shiro入门1


    前言:本文最终效果是基于ssm+shiro的一个简单的页面权限控制。源码地址:https://github.com/coVdiing/shiro.git

     

    1、Shiro是什么?
    Apache Shiro 是 Java 的一个安全框架。
     
    2.Shiro一些基本概念

     

    3.Shiro结合数据库。
    RBAC的概念
    Roles base access controll
    基于角色的权限控制
    或者
    Resources base access controll
    基于资源的权限控制
    通俗来说,你得获得对应的资源或者权限才能够进行访问
     
    表结构
    最简单的权限控制,是建立在 “用户” —— ”角色“ —— “权限” 之间的关系。
    其中用户和角色之间是多对多关系,角色和权限是多对多关系。
    在进行表的设计时,可以考虑在用户、角色、权限三张表的基础上,再建立
    用户-角色表,角色-权限表来维护他们之间的关系。
     所以,最简单的RBAC需要五张表来实现。
     
    4.编码,通过数据库的设计实现最简单的权限控制
    SQL:
     1 DROP DATABASE IF EXISTS shiro;
     2 CREATE DATABASE shiro DEFAULT CHARACTER SET utf8;
     3 USE shiro;
     4  
     5 drop table if exists user;
     6 drop table if exists role;
     7 drop table if exists permission;
     8 drop table if exists user_role;
     9 drop table if exists role_permission;
    10  
    11 create table user (
    12   id bigint auto_increment,
    13   name varchar(100),
    14   password varchar(100),
    15   constraint pk_users primary key(id)
    16 ) charset=utf8 ENGINE=InnoDB;
    17  
    18 create table role (
    19   id bigint auto_increment,
    20   name varchar(100),
    21   constraint pk_roles primary key(id)
    22 ) charset=utf8 ENGINE=InnoDB;
    23  
    24 create table permission (
    25   id bigint auto_increment,
    26   name varchar(100),
    27   constraint pk_permissions primary key(id)
    28 ) charset=utf8 ENGINE=InnoDB;
    29  
    30 create table user_role (
    31   uid bigint,
    32   rid bigint,
    33   constraint pk_users_roles primary key(uid, rid)
    34 ) charset=utf8 ENGINE=InnoDB;
    35  
    36 create table role_permission (
    37   rid bigint,
    38   pid bigint,
    39   constraint pk_roles_permissions primary key(rid, pid)
    40 ) charset=utf8 ENGINE=InnoDB;
    View Code
    插入数据
     1 INSERT INTO `permission` VALUES (1,'addProduct');
     2 INSERT INTO `permission` VALUES (2,'deleteProduct');
     3 INSERT INTO `permission` VALUES (3,'editProduct');
     4 INSERT INTO `permission` VALUES (4,'updateProduct');
     5 INSERT INTO `permission` VALUES (5,'listProduct');
     6 INSERT INTO `permission` VALUES (6,'addOrder');
     7 INSERT INTO `permission` VALUES (7,'deleteOrder');
     8 INSERT INTO `permission` VALUES (8,'editOrder');
     9 INSERT INTO `permission` VALUES (9,'updateOrder');
    10 INSERT INTO `permission` VALUES (10,'listOrder');
    11 INSERT INTO `role` VALUES (1,'admin');
    12 INSERT INTO `role` VALUES (2,'productManager');
    13 INSERT INTO `role` VALUES (3,'orderManager');
    14 INSERT INTO `role_permission` VALUES (1,1);
    15 INSERT INTO `role_permission` VALUES (1,2);
    16 INSERT INTO `role_permission` VALUES (1,3);
    17 INSERT INTO `role_permission` VALUES (1,4);
    18 INSERT INTO `role_permission` VALUES (1,5);
    19 INSERT INTO `role_permission` VALUES (1,6);
    20 INSERT INTO `role_permission` VALUES (1,7);
    21 INSERT INTO `role_permission` VALUES (1,8);
    22 INSERT INTO `role_permission` VALUES (1,9);
    23 INSERT INTO `role_permission` VALUES (1,10);
    24 INSERT INTO `role_permission` VALUES (2,1);
    25 INSERT INTO `role_permission` VALUES (2,2);
    26 INSERT INTO `role_permission` VALUES (2,3);
    27 INSERT INTO `role_permission` VALUES (2,4);
    28 INSERT INTO `role_permission` VALUES (2,5);
    29 INSERT INTO `role_permission` VALUES (3,6);
    30 INSERT INTO `role_permission` VALUES (3,7);
    31 INSERT INTO `role_permission` VALUES (3,8);
    32 INSERT INTO `role_permission` VALUES (3,9);
    33 INSERT INTO `role_permission` VALUES (3,10);
    34 INSERT INTO `user` VALUES (1,'zhang3','12345');
    35 INSERT INTO `user` VALUES (2,'li4','abcde');
    36 INSERT INTO `user_role` VALUES (1,1);
    37 INSERT INTO `user_role` VALUES (2,2);
    View Code
     
    User.java
    User类对应user表。
     1 public class User {
     2     private int id;
     3     private String name;
     4     private String password;
     5 
     6     public String getName() {
     7         return name;
     8     }
     9 
    10     public void setName(String name) {
    11         this.name = name;
    12     }
    13 
    14     public String getPassword() {
    15         return password;
    16     }
    17 
    18     public void setPassword(String password) {
    19         this.password = password;
    20     }
    21 }
    View Code
     
    Dao.java
    Dao类用于展示权限相关的查询,由于在数据库中限定好了user,roles,permission各自的关系,所以在Dao中不需要提供对User的增删改功能,因此也不需要实现Role,Permission类。
      1 public class Dao {
      2     private static final String URL = "jdbc:mysql://127.0.0.1:3306/shiro?characterEncoding=UTF-8";
      3     private static final String USERNAME = "root";
      4     private static final String PASSWORD = "root";
      5 
      6     public Dao() {
      7         try {
      8             Class.forName("com.mysql.jdbc.Driver");
      9         } catch (ClassNotFoundException e) {
     10             e.printStackTrace();
     11         }
     12     }
     13 
     14     public Connection getConnection() throws SQLException {
     15         return DriverManager.getConnection(URL, USERNAME, PASSWORD);
     16     }
     17 
     18     /**
     19      * 根据用户名查询密码
     20      * @param username
     21      * @return
     22      */
     23     public String getPassword(String username) {
     24         String sql = "SELECT password FROM user WHERE user.name = ? ";
     25         PreparedStatement pstmt = null;
     26         ResultSet rs = null;
     27         Connection c = null;
     28 
     29         try {
     30             c = getConnection();
     31             pstmt = c.prepareStatement(sql);
     32             pstmt.setString(1,username);
     33             rs = pstmt.executeQuery();
     34             while (rs.next()) {
     35                 return rs.getString("password");
     36             }
     37         } catch (SQLException e) {
     38             e.printStackTrace();
     39         } finally {
     40             try {
     41                 if (rs != null)
     42                     rs.close();
     43                 if (pstmt != null)
     44                     pstmt.close();
     45                 if (c != null)
     46                     c.close();
     47             } catch (SQLException e) {
     48                 e.printStackTrace();
     49             }
     50         }
     51         return null;
     52     }
     53 
     54     /**
     55      * 根据用户名查询角色
     56      * @param username
     57      * @return
     58      */
     59     public Set<String> listRoles(String username) {
     60         Set<String> roles = new HashSet<>();
     61         String sql = "SELECT r.name name FROM user u " +
     62                 "LEFT JOIN user_role u_r ON u.id = u_r.uid " +
     63                 "LEFT JOIN role r ON u_r.rid = r.id WHERE u.name = ?";
     64         PreparedStatement pstmt = null;
     65         ResultSet rs = null;
     66         Connection c = null;
     67 
     68         try {
     69             c = getConnection();
     70             pstmt = c.prepareStatement(sql);
     71             pstmt.setString(1,username);
     72             rs = pstmt.executeQuery();
     73             while (rs.next()) {
     74                 roles.add(rs.getString("name"));
     75             }
     76         } catch (SQLException e) {
     77             e.printStackTrace();
     78         } finally {
     79             try {
     80                 if (rs != null)
     81                     rs.close();
     82                 if (pstmt != null)
     83                     pstmt.close();
     84                 if (c != null)
     85                     c.close();
     86             } catch (SQLException e) {
     87                 e.printStackTrace();
     88             }
     89         }
     90         return roles;
     91     }
     92 
     93     /**
     94      * 根据用户名列出对应的权限
     95      * @param username
     96      * @return
     97      */
     98     public Set<String> listPermissions(String username) {
     99         //五张表的连接
    100         String sql = "SELECT p.name name FROM user u " +
    101                 "LEFT JOIN user_role u_r ON u.id = u_r.uid " +
    102                 "LEFT JOIN role r ON u_r.rid = r.id " +
    103                 "LEFT JOIN role_permission r_p ON r.id = r_p.rid " +
    104                 "LEFT JOIN permission p ON r_p.pid = p.id WHERE u.name = ?";
    105         Set<String> permissions = new HashSet<>();
    106         PreparedStatement pstmt = null;
    107         ResultSet rs = null;
    108         Connection c = null;
    109 
    110         try {
    111             c = getConnection();
    112             pstmt = c.prepareStatement(sql);
    113             pstmt.setString(1,username);
    114             rs = pstmt.executeQuery();
    115             while (rs.next()) {
    116                 permissions.add(rs.getString("name"));
    117             }
    118         } catch (SQLException e) {
    119             e.printStackTrace();
    120         } finally {
    121             try {
    122                 if (rs != null)
    123                     rs.close();
    124                 if (pstmt != null)
    125                     pstmt.close();
    126                 if (c != null)
    127                     c.close();
    128             } catch (SQLException e) {
    129                 e.printStackTrace();
    130             }
    131         }
    132         return permissions;
    133     }
    134 
    135 }
    View Code
    main方法
     1 Dao dao = new Dao();
     2 System.out.println("zhang3");
     3 System.out.println(dao.getPassword("zhang3"));
     4 System.out.println(dao.listRoles("zhang3"));
     5 System.out.println(dao.listPermissions("zhang3"));
     6 
     7 System.out.println("--------------------------------------------------------------vi--------------------------------------------------------------");
     8 System.out.println("li4");
     9 System.out.println(dao.getPassword("li4"));
    10 System.out.println(dao.listRoles("li4"));
    11 System.out.println(dao.listPermissions("li4"));
    View Code
    结果如下图:
    体验过简单版本的RBAC之后,接下来学习下Shiro是怎么做的。
     
    5.Shiro的认证过程
    1.创建一个maven工程,这个就不说了。
    2.导入依赖,pom.xml
     1 <dependencies>
     2     <!--shiro核心-->
     3     <dependency>
     4         <groupId>org.apache.shiro</groupId>
     5         <artifactId>shiro-core</artifactId>
     6         <version>1.4.0</version>
     7     </dependency>
     8 
     9     <dependency>
    10         <groupId>junit</groupId>
    11         <artifactId>junit</artifactId>
    12         <version>RELEASE</version>
    13     </dependency>
    14 
    15     <dependency>
    16         <groupId>org.slf4j</groupId>
    17         <artifactId>slf4j-nop</artifactId>
    18         <version>1.7.28</version>
    19     </dependency>
    20 </dependencies>
    View Code

    shiro-core是shiro的核心,junit则是为了单元测试,slf4j-nop,尽管网上查了很多博客,包括shiro的入门视频里都没有提到这个包,但是实际使用过程中,没有加上就一直报错。

     1 public class testAuthentication {
     2     SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();
     3 
     4     @Before
     5     public void addUser() {
     6         simpleAccountRealm.addAccount("Mark","12345");
     7     }
     8 
     9     @Test
    10     public void authentication(){
    11         //1.创建SecurityManager环境
    12         DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
    13         //2.设置环境
    14         SecurityUtils.setSecurityManager(defaultSecurityManager);
    15         //3.设置Realm
    16         defaultSecurityManager.setRealm(simpleAccountRealm);
    17         //4.获取主体
    18         Subject subject = SecurityUtils.getSubject();
    19         //5.获取用户名密码
    20         UsernamePasswordToken token = new UsernamePasswordToken("Mark", "12345");
    21         //6.认证
    22         try {
    23             subject.login(token);
    24         } catch (Exception e) {
    25             e.printStackTrace();
    26         }
    27         System.out.println("isAuthentication:"+subject.isAuthenticated());
    28         //登出
    29         subject.logout();
    30         System.out.println("isAuthentication:"+subject.isAuthenticated());
    31     }
    32 }
    View Code

    6.授权过程
    与上面的认证过程基本一致,将之前的代码稍作修改
     1 public class testAuthentication {
     2     SimpleAccountRealm simpleAccountRealm = new SimpleAccountRealm();
     3 
     4     @Before
     5     public void addUser() {
     6         //在password后可以添加角色参数,该参数是一个可变参数
     7         simpleAccountRealm.addAccount("Mark","12345","admin","user");
     8     }
     9 
    10     @Test
    11     public void authentication(){
    12         //1.创建SecurityManager环境
    13         DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
    14         //2.设置环境
    15         SecurityUtils.setSecurityManager(defaultSecurityManager);
    16         //3.设置Realm
    17         defaultSecurityManager.setRealm(simpleAccountRealm);
    18         //4.获取主体
    19         Subject subject = SecurityUtils.getSubject();
    20         //5.获取用户名密码
    21         UsernamePasswordToken token = new UsernamePasswordToken("Mark", "12345");
    22         //6.认证
    23         try {
    24             subject.login(token);
    25 
    26         } catch (Exception e) {
    27             e.printStackTrace();
    28         }
    29         System.out.println("isAuthentication:"+subject.isAuthenticated());
    30         subject.checkRoles("admin1","user");
    31     }
    32 }
    View Code
    如果checkRole方法中的参数与simpleAccountRealm.addAccount()方法中角色的参数一致,程序正常运行,如果不一致,则会发生UnauthorizedException。
     
     
    7.IniRealm
    IniRealm是Shiro的内置Realm之一,相比之前的SimpleAccountRealm,不仅可以用于认证和角色查询,还增加了权限相关的功能。下面使用IniRealm来进行简单的认证和权限查询。
     
    首先在resources文件夹(类路径下)中创建一个ini文件,用于存储用户、角色、权限相关的数据
    account.ini
    1 #用户名:Mark 密码:12345 角色:admin
    2 [users]
    3 Mark=12345,admin
    4 #admin角色拥有 对user的add和delete权限
    5 [roles]
    6 admin=user:delete,user:add
    View Code
     
    IniRealmDemo.java
     1 public class IniRealmDemo {
     2     @Test
     3     public void testini() {
     4         IniRealm iniRealm = new IniRealm("classpath:account.ini");//realm从数据源获取相关数据
     5         //1.创建SecurityManager环境
     6         DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
     7         //2.设置环境
     8         defaultSecurityManager.setRealm(iniRealm);
     9         //3.设置Realm
    10         SecurityUtils.setSecurityManager(defaultSecurityManager);
    11         //4.获得主体
    12         Subject subject = SecurityUtils.getSubject();
    13         //5.获取用户名密码
    14         UsernamePasswordToken token = new UsernamePasswordToken("Mark", "12345");
    15         //6.登录
    16         subject.login(token);
    17         //7.检测用户是否有admin角色
    18         subject.checkRole("admin");
    19         //8.检测用户是否有user:add,user:delete权限
    20         subject.checkPermissions("user:add","user:delete");
    21     }
    22 }
    View Code
    8.JdbcRealm
    同样是Shiro的内置Realm,可以连接数据库使用。
    为了示范它的基本用法,首先导入mysql连接驱动以及druid数据源依赖。
     1 <!--mysql驱动-->
     2 <dependency>
     3     <groupId>mysql</groupId>
     4     <artifactId>mysql-connector-java</artifactId>
     5     <version>5.1.47</version>
     6 </dependency>
     7 <!--数据源-->
     8 <dependency>
     9     <groupId>com.alibaba</groupId>
    10     <artifactId>druid</artifactId>
    11     <version>1.1.20</version>
    12 </dependency>
    View Code
     
    JdbcRealmDemo.java
     1 public class JdbcRealmDemo {
     2     DruidDataSource dataSource = new DruidDataSource();
     3     {
     4         dataSource.setUrl("jdbc:mysql://localhost:3306/shiro");
     5         dataSource.setUsername("root");
     6         dataSource.setPassword("root");
     7     }
     8 
     9     @Test
    10     public void testJdbcRealm() {
    11         JdbcRealm jdbcRealm = new JdbcRealm();
    12         String sql = "SELECT password FROM user WHERE name = ?";
    13         //调用自定义的查询语句,进行认证查询
    14         jdbcRealm.setAuthenticationQuery(sql);
    15         //设置数据源
    16         jdbcRealm.setDataSource(dataSource);
    17         //1.创建SecurityManager环境
    18         DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
    19         //2.设置环境
    20         defaultSecurityManager.setRealm(jdbcRealm);
    21         //3.设置Realm
    22         SecurityUtils.setSecurityManager(defaultSecurityManager);
    23         //4.获取主体
    24         Subject subject = SecurityUtils.getSubject();
    25         //5.获取用户名密码
    26         UsernamePasswordToken token = new UsernamePasswordToken("zhang3", "12345");
    27         //6.认证
    28         subject.login(token);
    29         System.out.println(subject.isAuthenticated());
    30     }
    31 }
    View Code
     
    执行结果:
    如果修改成没有记录在数据库中的用户名:mayun
    发生未知账户异常,就算马云来了也不好使。
    如果修改成不对应的密码:admin
    发生错误的凭证异常
     
    进行角色验证之前,需要先编写对应的查询角色SQL,然后调用对应的查询方法
     1 public void testJdbcRealm() {
     2     JdbcRealm jdbcRealm = new JdbcRealm();
     3     String sql = "SELECT password FROM user WHERE name = ?";
     4     String roleSQL = "SELECT r.name FROM user u" +
     5             " LEFT JOIN user_role u_r ON u.id = u_r.uid" +
     6             " LEFT JOIN role r ON u_r.rid = r.id WHERE u.name = ?";
     7 
     8     //调用自定义的查询语句,进行认证查询
     9     jdbcRealm.setAuthenticationQuery(sql);
    10     //角色查询
    11     jdbcRealm.setUserRolesQuery(roleSQL);
    12     //设置数据源
    13     jdbcRealm.setDataSource(dataSource);
    14     //1.创建SecurityManager环境
    15     DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
    16     //2.设置环境
    17     defaultSecurityManager.setRealm(jdbcRealm);
    18     //3.设置Realm
    19     SecurityUtils.setSecurityManager(defaultSecurityManager);
    20     //4.获取主体
    21     Subject subject = SecurityUtils.getSubject();
    22     //5.获取用户名密码
    23     UsernamePasswordToken token = new UsernamePasswordToken("zhang3", "12345");
    24     //6.认证
    25     subject.login(token);
    26     System.out.println(subject.isAuthenticated());
    27     //查询角色
    28     System.out.println("zhang3 -> admin:"+subject.hasRole("admin"));
    29 }   
    View Code
     
    执行结果:
     
    9.自定义Realm
    JdbcRealm是AuthorizingRealm的子类,我们要实现自定义Realm就需要去继承这个抽象类。
    这个类中有两个抽象方法需要我们去实现:
    • doGetAuthenticationInfo() 用于认证
    • doGetAuthorizationInfo() 用于授权
     
    DatabaseRealm.java
     1 public class DatabaseRealm extends AuthorizingRealm {
     2 
     3     protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
     4         //能进入到这里,表示账号已经通过认证了
     5         String username = (String) principals.getPrimaryPrincipal();
     6         //通过Dao获取角色和权限
     7         Set<String> permissions = new Dao().listPermissions(username);
     8         Set<String> roles = new Dao().listRoles(username);
     9         //授权对象
    10         SimpleAuthorizationInfo sai =  new SimpleAuthorizationInfo();
    11         //把通过Dao获取到的角色和权限都放进去
    12         sai.setStringPermissions(permissions);
    13         sai.setRoles(roles);
    14         return sai;
    15     }
    16 
    17     protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    18         //获取账号密码
    19         UsernamePasswordToken upt = (UsernamePasswordToken) token;
    20         String username = token.getPrincipal().toString();
    21         String password = new String(upt.getPassword());
    22         //获取数据库中的密码
    23         String passwordInDB = new Dao().getPassword(username);
    24         //如果为空就是账号不存在,如果不相同就是密码错误,但是都抛出AuthenticationException,而不抛出具体错误原因,以防给破解者提供帮助信息
    25         if (null == passwordInDB || !password.equals(passwordInDB)) {
    26             throw new AuthenticationException();
    27         }
    28         //认证信息里存放账号和密码,getName()是当前Realm的继承方法,通常返回当前类名:DatabaseRealm
    29         SimpleAuthenticationInfo sai = new SimpleAuthenticationInfo(username,password,getName());
    30         return sai;
    31     }
    32 }
    View Code
    自定义Realm虽然由我们定义,但是却是由Shiro自动调用(使用subject时)。
    需要注意的几点:
    认证时获取用户名是通过token对象的getPrincipal()方法,获取密码是通过UsernamePasswordToken对象的getPassword方法。存放认证信息,返回的对象是AuthenticationInfo对象,构造方法需要传入
    username,password,以及当前类名
     
    授权时,通过PrincipalCollection 对象的getPrimaryPrincipal()方法获取用户名,将获取到的权限和角色数据存入AuthorizationInfo 对象返回。
     
     
    10.加密
     
    md5加密
    在前面的例子中,密码都是明文的,存在巨大风险,通常我们需要将密码加密以后存储到数据库中,采用的加密手段通常都是不可逆的。
    什么是不可逆的加密呢?简单来说,输入字符串"123",假设通过计算得到密文"02CB962AC59075B964B07152D2",但是反过来却不能通过计算得到原密码,称这种加密是不可逆的。
     
    md5就是这样的一种加密方法。
    把加密后的密文存在数据库中,这样下次登录时,把登录的数据加密以后再进行比较,就可以判断是否正确了,同时避免了暴露密码的风险
     
    1 public void testMd5() {
    2     String password = "123";
    3     String encryptPwd = new Md5Hash(password).toString();
    4     System.out.println(encryptPwd);//结果:202cb962ac59075b964b07152d234b70
    5 }
    View Code
     
    加盐
    尽管通过md5是不可逆的加密,但仍然存在一些缺陷,比如说,假如两个人的密码都是"123",那么得到的密文就是相同的,通过这个思路,就可以使用穷举法对一些比较常用的密码进行破解。
     
    考虑到这个原因,我们需要在加密的过程中做“加盐”处理,“盐”在这里可以理解为加密的程度,相同的密码,通过不同程度的加密,也会得到不同的密文。
     
    举个例子,当密码都是123时,在加密之前,给123加上不同的随机值,再进行加密,就会得到不同的密文,这里的随机值就是我们的“盐”,不同的用户对应不同的盐,而盐也需要存进数据库里,与此同时,加密的次数也会导致密文的不同。下面演示如何用Shiro自带的工具类,通过盐来进行md5加密。
     
    1 public void testSalt() {
    2     String password = "123";
    3     String salt = new SecureRandomNumberGenerator().nextBytes().toString();
    4     int times = 2;
    5     String algorithmName = "md5";
    6     String encryptPwd = new SimpleHash(algorithmName, password, salt, times).toString();
    7     System.out.printf("算法为:%s ,明文:%s ,盐:%s ,加密次数:%d ,结果:
    %s",algorithmName,password,salt,times,encryptPwd);
    8 }
    View Code
    结果:
     
    为了存储字段"盐",修改表结构,加入"salt"字段。
    alter table user add(salt varchar(100));
     
    首先在Dao中增加一个addUser(name,password)方法用于注册。
     1 /**
     2  * 新增用户
     3  * @param name
     4  * @param password
     5  */
     6 public void addUser(String name, String password) {
     7     String sql = "INSERT INTO user VALUES(null,?,?,?)";
     8     String salt = new SecureRandomNumberGenerator().nextBytes().toString();//盐量随机
     9     String encryptPwd = new SimpleHash("md5",password,salt,2).toString();   //md5加密算法,加密2次
    10     try {
    11         PreparedStatement pstmt = conn.prepareStatement(sql);
    12         pstmt.setString(1, name);
    13         pstmt.setString(2, encryptPwd);
    14         pstmt.setString(3, salt);
    15         pstmt.execute();
    16     } catch (Exception e) {
    17         e.printStackTrace();
    18     }
    19 }
    View Code
    增加一个getUser()方法,其中不仅包括加密后的密码,还有盐。
     1 /**
     2  * 获取用户
     3  * @return
     4  */
     5 public User getUser(String username) {
     6     String sql = "SELECT * FROM user WHERE name = ?";
     7     User user = null;
     8     try {
     9         PreparedStatement pstmt = conn.prepareStatement(sql);
    10         pstmt.setString(1,username);
    11         ResultSet rs = pstmt.executeQuery();
    12         if(rs.next()) {
    13             user = new User();
    14             user.setId(rs.getInt("id"));
    15             user.setName(rs.getString("name"));
    16             user.setPassword(rs.getString("password"));
    17             user.setSalt(rs.getString("salt"));
    18         }
    19     } catch (Exception e) {
    20         e.printStackTrace();
    21     }
    22     return user;
    23 }
    View Code
    对DatabaseRealm进行相应的修改,得到传入的用户名密码以后,从数据库中获取对应的密码和盐,对输入密码加盐之后与数据库中的密码比对,最后将用户名密码封装到认证信息中。
     1 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
     2     //获取账号密码
     3     UsernamePasswordToken upt = (UsernamePasswordToken) token;
     4     String username = token.getPrincipal().toString();
     5     String password = new String(upt.getPassword());
     6     String encryptPassword = "";
     7     //获取数据库中的密码
     8     User user = new Dao().getUser(username);
     9     if(user != null) {
    10         String passwordInDB = user.getPassword();
    11         String salt = user.getSalt();
    12         //将传入的密码进行加盐
    13         encryptPassword = new SimpleHash("md5",password,user.getSalt(),2).toString();
    14         System.out.println("盐:"+user.getSalt());
    15         System.out.println("加密后的密码:"+encryptPassword);
    16         System.out.println("原密码:"+user.getPassword());
    17     }
    18     //如果为空就是账号不存在,如果不相同就是密码错误,但是都抛出AuthenticationException,而不抛出具体错误原因,以防给破解者提供帮助信息
    19     if (null == user || !encryptPassword.equals(user.getPassword())) {
    20         System.out.println("抛了异常!");
    21         throw new AuthenticationException();
    22     }
    23     //认证信息里存放账号和密码,getName()是当前Realm的继承方法,通常返回当前类名:DatabaseRealm
    24     SimpleAuthenticationInfo sai = new SimpleAuthenticationInfo(username,password,getName());
    25     return sai;
    26 }
    View Code
     

    11.Shiro整合SSM

    先贴一个项目结构目录。
    1. 创建maven工程。这个就不细说了。
    2. 在pom.xml中导入对应坐标。
      1 <?xml version="1.0" encoding="UTF-8"?>
      2 
      3 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      4          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      5     <modelVersion>4.0.0</modelVersion>
      6 
      7     <groupId>vi</groupId>
      8     <artifactId>shiro</artifactId>
      9     <version>1.0-SNAPSHOT</version>
     10     <packaging>war</packaging>
     11 
     12     <name>shiro Maven Webapp</name>
     13     <!-- FIXME change it to the project's website -->
     14     <url>http://www.example.com</url>
     15 
     16     <properties>
     17         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
     18         <maven.compiler.source>1.7</maven.compiler.source>
     19         <maven.compiler.target>1.7</maven.compiler.target>
     20 
     21         <!-- lib verion -->
     22         <junit.version>4.11</junit.version>
     23         <spring.version>5.1.9.RELEASE</spring.version>
     24         <mybatis.spring.version>1.2.2</mybatis.spring.version>
     25         <mysql.connector.version>5.1.47</mysql.connector.version>
     26         <slf4j.version>1.6.6</slf4j.version>
     27         <log4j.version>1.2.12</log4j.version>
     28         <druid.version>1.0.5</druid.version>
     29         <jstl.version>1.2</jstl.version>
     30         <commons.fileupload.version>1.3.1</commons.fileupload.version>
     31         <shiro.version>1.4.0</shiro.version>
     32     </properties>
     33 
     34     <dependencies>
     35         <dependency>
     36             <groupId>junit</groupId>
     37             <artifactId>junit</artifactId>
     38             <version>4.12</version>
     39         </dependency>
     40 
     41         <!-- mysql-connector -->
     42         <dependency>
     43             <groupId>mysql</groupId>
     44             <artifactId>mysql-connector-java</artifactId>
     45             <version>${mysql.connector.version}</version>
     46         </dependency>
     47 
     48         <!-- DruidDataSource -->
     49         <dependency>
     50             <groupId>com.alibaba</groupId>
     51             <artifactId>druid</artifactId>
     52             <version>${druid.version}</version>
     53         </dependency>
     54 
     55         <!-- shiro -->
     56         <dependency>
     57             <groupId>org.apache.shiro</groupId>
     58             <artifactId>shiro-spring</artifactId>
     59             <version>${shiro.version}</version>
     60         </dependency>
     61         <dependency>
     62             <groupId>org.apache.shiro</groupId>
     63             <artifactId>shiro-ehcache</artifactId>
     64             <version>${shiro.version}</version>
     65         </dependency>
     66         <dependency>
     67             <groupId>org.apache.shiro</groupId>
     68             <artifactId>shiro-core</artifactId>
     69             <version>${shiro.version}</version>
     70         </dependency>
     71         <dependency>
     72             <groupId>org.apache.shiro</groupId>
     73             <artifactId>shiro-web</artifactId>
     74             <version>${shiro.version}</version>
     75         </dependency>
     76         <dependency>
     77             <groupId>org.apache.shiro</groupId>
     78             <artifactId>shiro-quartz</artifactId>
     79             <version>${shiro.version}</version>
     80         </dependency>
     81         <!-- shiro -->
     82 
     83         <!-- log start -->
     84         <dependency>
     85             <groupId>log4j</groupId>
     86             <artifactId>log4j</artifactId>
     87             <version>${log4j.version}</version>
     88         </dependency>
     89         <dependency>
     90             <groupId>org.slf4j</groupId>
     91             <artifactId>slf4j-api</artifactId>
     92             <version>${slf4j.version}</version>
     93         </dependency>
     94         <dependency>
     95             <groupId>org.slf4j</groupId>
     96             <artifactId>slf4j-log4j12</artifactId>
     97             <version>${slf4j.version}</version>
     98         </dependency>
     99         <!-- log end -->
    100 
    101         <dependency>
    102             <groupId>commons-logging</groupId>
    103             <artifactId>commons-logging</artifactId>
    104             <version>1.1.3</version>
    105         </dependency>
    106 
    107         <dependency>
    108             <groupId>javax.servlet</groupId>
    109             <artifactId>jstl</artifactId>
    110             <version>${jstl.version}</version>
    111         </dependency>
    112 
    113         <dependency>
    114             <groupId>javax.servlet</groupId>
    115             <artifactId>javax.servlet-api</artifactId>
    116             <version>4.0.1</version>
    117         </dependency>
    118 
    119         <!--Spring -->
    120         <dependency>
    121             <groupId>org.springframework</groupId>
    122             <artifactId>spring-core</artifactId>
    123             <version>${spring.version}</version>
    124         </dependency>
    125 
    126         <dependency>
    127             <groupId>org.springframework</groupId>
    128             <artifactId>spring-webmvc</artifactId>
    129             <version>${spring.version}</version>
    130         </dependency>
    131 
    132         <dependency>
    133             <groupId>org.springframework</groupId>
    134             <artifactId>spring-context</artifactId>
    135             <version>${spring.version}</version>
    136         </dependency>
    137 
    138         <dependency>
    139             <groupId>org.springframework</groupId>
    140             <artifactId>spring-web</artifactId>
    141             <version>${spring.version}</version>
    142         </dependency>
    143 
    144         <dependency>
    145             <groupId>org.springframework</groupId>
    146             <artifactId>spring-jdbc</artifactId>
    147             <version>${spring.version}</version>
    148         </dependency>
    149 
    150         <dependency>
    151             <groupId>org.springframework</groupId>
    152             <artifactId>spring-test</artifactId>
    153             <version>${spring.version}</version>
    154         </dependency>
    155         <!--Spring end -->
    156 
    157         <dependency>
    158             <groupId>org.mybatis</groupId>
    159             <artifactId>mybatis</artifactId>
    160             <version>3.5.2</version>
    161         </dependency>
    162 
    163         <dependency>
    164             <groupId>org.mybatis</groupId>
    165             <artifactId>mybatis-spring</artifactId>
    166             <version>2.0.2</version>
    167         </dependency>
    168     </dependencies>
    169 
    170     <build>
    171         <finalName>shiro</finalName>
    172         <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
    173             <plugins>
    174                 <plugin>
    175                     <artifactId>maven-clean-plugin</artifactId>
    176                     <version>3.1.0</version>
    177                 </plugin>
    178                 <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
    179                 <plugin>
    180                     <artifactId>maven-resources-plugin</artifactId>
    181                     <version>3.0.2</version>
    182                 </plugin>
    183                 <plugin>
    184                     <artifactId>maven-compiler-plugin</artifactId>
    185                     <version>3.8.0</version>
    186                 </plugin>
    187                 <plugin>
    188                     <artifactId>maven-surefire-plugin</artifactId>
    189                     <version>2.22.1</version>
    190                 </plugin>
    191                 <plugin>
    192                     <artifactId>maven-war-plugin</artifactId>
    193                     <version>3.2.2</version>
    194                 </plugin>
    195                 <plugin>
    196                     <artifactId>maven-install-plugin</artifactId>
    197                     <version>2.5.2</version>
    198                 </plugin>
    199                 <plugin>
    200                     <artifactId>maven-deploy-plugin</artifactId>
    201                     <version>2.8.2</version>
    202                 </plugin>
    203             </plugins>
    204         </pluginManagement>
    205     </build>
    206 </project>
    View Code
    3.配置web.xml。
     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
     3          xmlns="http://java.sun.com/xml/ns/javaee" 
     4          xmlns:web="http://java.sun.com/xml/ns/javaee" 
     5          xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
     6    
     7    <!-- spring的配置文件-->
     8    <context-param>
     9       <param-name>contextConfigLocation</param-name>
    10       <param-value>
    11          classpath:applicationContext.xml
    12       </param-value>
    13    </context-param>
    14    <listener>
    15       <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    16    </listener>
    17    
    18    
    19    <!-- spring mvc核心:分发servlet -->
    20    <servlet>
    21       <servlet-name>mvc-dispatcher</servlet-name>
    22       <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    23       <!-- spring mvc的配置文件 -->
    24       <init-param>
    25          <param-name>contextConfigLocation</param-name>
    26          <param-value>classpath:springMVC.xml</param-value>
    27       </init-param>
    28       <load-on-startup>1</load-on-startup>
    29    </servlet>
    30    <servlet-mapping>
    31       <servlet-name>mvc-dispatcher</servlet-name>
    32       <url-pattern>/</url-pattern>
    33    </servlet-mapping>
    34    
    35    <!-- Shiro配置 -->
    36    <filter>
    37       <filter-name>shiroFilter</filter-name>
    38       <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    39       <init-param>
    40          <param-name>targetFilterLifecycle</param-name>
    41          <param-value>true</param-value>
    42       </init-param>
    43    </filter>
    44    <filter-mapping>
    45       <filter-name>shiroFilter</filter-name>
    46       <url-pattern>/*</url-pattern>
    47    </filter-mapping>  
    48 
    49 </web-app>
    View Code
     
    4.Spring配置文件
    applicationContext.xml
      1 <?xml version="1.0" encoding="UTF-8"?>
      2 <beans xmlns="http://www.springframework.org/schema/beans"
      3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      4        xmlns:context="http://www.springframework.org/schema/context"
      5        xmlns:util="http://www.springframework.org/schema/util"
      6        xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
      7      http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
      8     http://www.springframework.org/schema/util
      9     http://www.springframework.org/schema/util/spring-util.xsd">
     10     <context:component-scan base-package="com.vi.service"/>
     11 
     12     <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
     13         <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
     14         <property name="url" value="jdbc:mysql://localhost:3306/shiro?characterEncoding=UTF-8&amp;serverTimezone=UTC"/>
     15         <property name="username" value="root"/>
     16         <property name="password" value="root"/>
     17     </bean>
     18 
     19     <bean id="sqlSession" class="org.mybatis.spring.SqlSessionFactoryBean">
     20         <property name="typeAliasesPackage" value="com.vi.entity"/>
     21         <property name="dataSource" ref="dataSource"/>
     22         <property name="mapperLocations" value="classpath:mapper/*Mapper.xml"/>
     23     </bean>
     24 
     25     <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
     26         <property name="basePackage" value="com.vi.mapper"/>
     27     </bean>
     28 
     29     <!-- 提供shiro的相关配置 -->
     30     <bean id="databaseRealm" class="com.vi.Realm.DatabaseRealm"/>
     31 
     32     <!-- 配置shiro的过滤器工厂类,id- shiroFilter要和我们在web.xml中配置的过滤器一致 -->
     33     <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
     34         <!-- 调用我们配置的权限管理器 -->
     35         <property name="securityManager" ref="securityManager"/>
     36         <!-- 配置我们的登录请求地址 -->
     37         <property name="loginUrl" value="/login"/>
     38         <!-- 如果您请求的资源不再您的权限范围,则跳转到/403请求地址 -->
     39         <property name="unauthorizedUrl" value="/unauthorized"/>
     40         <!-- 退出 -->
     41         <property name="filters">
     42             <util:map>
     43                 <entry key="logout" value-ref="logoutFilter"/>
     44             </util:map>
     45         </property>
     46         <!-- 权限配置 -->
     47         <property name="filterChainDefinitions">
     48             <value>
     49                 <!-- anon表示此地址不需要任何权限即可访问 -->
     50                 /login=anon
     51                 /index=anon
     52                 /static/**=anon
     53                 /doLogout=logout
     54                 <!--所有的请求(除去配置的静态资源请求或请求地址为anon的请求)都要通过登录验证,如果未登录则跳到/login -->
     55                 /** = authc
     56             </value>
     57         </property>
     58     </bean>
     59 
     60     <!-- 退出过滤器 -->
     61     <bean id="logoutFilter" class="org.apache.shiro.web.filter.authc.LogoutFilter">
     62         <property name="redirectUrl" value="/index"/>
     63     </bean>
     64 
     65     <!-- 会话ID生成器 -->
     66     <bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/>
     67 
     68     <!--会话Cookie模板,关闭浏览器立即失效-->
     69     <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
     70         <constructor-arg value="sid"/>
     71         <property name="httpOnly" value="true"/>
     72         <property name="maxAge" value="-1"/>
     73     </bean>
     74 
     75     <!--会话DAO-->
     76     <bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
     77         <property name="sessionIdGenerator" ref="sessionIdGenerator"/>
     78     </bean>
     79 
     80     <!--会话调度验证器,每30分钟执行一次验证,设定会话超时保存-->
     81     <bean name="sessionValidationSchduler"
     82           class="org.apache.shiro.session.mgt.ExecutorServiceSessionValidationScheduler">
     83         <property name="interval" value="1800000"/>
     84         <property name="sessionManager" ref="sessionManager"/>
     85     </bean>
     86 
     87     <!--会话管理器-->
     88     <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
     89         <!--全局会话超时时间,默认30分钟,单位毫秒-->
     90         <property name="globalSessionTimeout" value="1800000"/>
     91         <property name="deleteInvalidSessions" value="true"/>
     92         <property name="sessionValidationSchedulerEnabled" value="true"/>
     93         <property name="sessionValidationScheduler" ref="sessionValidationSchduler"/>
     94         <property name="sessionDAO" ref="sessionDAO"/>
     95         <property name="sessionIdCookieEnabled" value="true"/>
     96         <property name="sessionIdCookie" ref="sessionIdCookie"/>
     97     </bean>
     98 
     99     <!--安全管理器-->
    100     <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    101         <property name="realm" ref="databaseRealm"/>
    102         <property name="sessionManager" ref="sessionManager"/>
    103     </bean>
    104 
    105     <!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) -->
    106     <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    107         <property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
    108         <property name="arguments" ref="securityManager"/>
    109     </bean>
    110 
    111     <!--保证了Shiro内部LifeCycle函数的bean执行-->
    112     <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
    113 
    114 </beans>
    View Code
    简单说一下这个配置
    <context:component-scan base-package="com.vi.service"/>
    扫描base-package中配置的包,在这个包下面,可以使用@Autowired对变量进行自动注入,由Spring为我们提供实例。
    当然,即使是没有被扫描的包,如果某个类在applicationContext.xml中注册了,也可以使用@Autowired进行自动注入。
     
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    配置数据源。
     
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionFactoryBean">
     
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    这两个bean是对Mybatis的整合。让Mybatis可以使用我们上面配置的数据源,同时也配置了实体类,Mapper接口,Mapper.xml的位置,进而为我们提供之后需要的CRUD功能。
     
    下面的部分就是Shiro的配置了。
    <!-- 提供shiro的相关配置 -->
    <bean id="databaseRealm" class="com.vi.Realm.DatabaseRealm"/>
    这个是由我们自定义的Realm类,Shiro的认证和授权就是在这里进行的,代码是我们完成的,但是调用却是由Shiro自己进行。
     
     1 <!-- 配置shiro的过滤器工厂类,id- shiroFilter要和我们在web.xml中配置的过滤器一致 -->
     2 <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
     3     <!-- 调用我们配置的权限管理器 -->
     4     <property name="securityManager" ref="securityManager"/>
     5     <!-- 配置我们的登录请求地址 -->
     6     <property name="loginUrl" value="/login"/>
     7     <!-- 如果您请求的资源不再您的权限范围,则跳转到/403请求地址 -->
     8     <property name="unauthorizedUrl" value="/unauthorized"/>
     9     <!-- 退出 -->
    10     <property name="filters">
    11         <util:map>
    12             <entry key="logout" value-ref="logoutFilter"/>
    13         </util:map>
    14     </property>
    15     <!-- 权限配置 -->
    16     <property name="filterChainDefinitions">
    17         <value>
    18             <!-- anon表示此地址不需要任何权限即可访问 -->
    19             /login=anon
    20             /index=anon
    21             /static/**=anon
    22             /doLogout=logout
    23             <!--所有的请求(除去配置的静态资源请求或请求地址为anon的请求)都要通过登录验证,如果未登录则跳到/login -->
    24             /** = authc
    25         </value>
    26     </property>
    27 </bean>
    View Code
    这个shiroFilter的命名必须和web.xml中的shiroFilter过滤器保持一致。
    这里详细配置了我们需要的权限,登录请求地址,错误跳转地址等。同时还引用了securityManager
     
    <!--安全管理器-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    <property name="realm" ref="databaseRealm"/>
    <property name="sessionManager" ref="sessionManager"/>
    </bean>
    securityManager中配置了两个属性,databaseRealm,用于认证和授权,另一个则是sessionManager,这个xml中有很多bean都是会话管理器的成员,这个具体的作用我还不清楚T_T。
     
    <!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) -->
    <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
    <property name="arguments" ref="securityManager"/>
    </bean>
     
    这一步的作用,相当于帮我们写了:
    DefaultSecurityManager securityManager = new DefaultSecurityManager(); SecurityUtils.setSecurityManager(securityManager);
     
    做了准备环境的工作。
     
    5.
    springMVC.xml
    a. springmvc的基本配置
    b. 增加了对shiro的支持
    这样可以在控制器Controller上,使用像@RequireRole 这样的注解,来表示某个方法必须有相关的角色才能访问
    c. 指定了异常处理类DefaultExceptionHandler,这样当访问没有权限的资源的时候,就会跳到统一的页面去显示错误信息
     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans xmlns="http://www.springframework.org/schema/beans"
     3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     4        xmlns:context="http://www.springframework.org/schema/context"
     5        xmlns:mvc="http://www.springframework.org/schema/mvc"
     6        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     7         http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
     8         http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd">
     9     <context:component-scan base-package="com.vi.controller">
    10         <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    11     </context:component-scan>
    12 
    13     <mvc:annotation-driven/>
    14 
    15     <mvc:default-servlet-handler/>
    16 
    17     <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    18         <property name="prefix" value="/WEB-INF/jsp/"/>
    19         <property name="suffix" value=".jsp"/>
    20     </bean>
    21 
    22     <!--启用shiro注解-->
    23     <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
    24         <property name="proxyTargetClass" value="true"/>
    25     </bean>
    26     <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
    27         <property name="securityManager" ref="securityManager"/>
    28     </bean>
    29 
    30     <!--控制器异常处理-->
    31     <bean id="exceptionHandlerExceptionResolver" class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver">
    32 
    33     </bean>
    34 
    35     <bean class="com.vi.exception.DefaultExceptionHandler"/>
    36 </beans>
    View Code
    6.log4j.properties
    显示数据库的SQL
    # Global logging configuration
    log4j.rootLogger=ERROR, stdout
    # MyBatis logging configuration...
    log4j.logger.com.how2java=TRACE
    # Console output...
    log4j.appender.stdout=org.apache.log4j.ConsoleAppender
    log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
    log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
    7.
    PageController.java
    因为使用 ssm,所以jsp通常都会放在WEB-INF/jsp 下面,而这个位置是无法通过浏览器直接访问的,所以就会专门做这么一个类,便于访问这些jsp。
    比如要访问WEB-INF/jsp/index.jsp文件,那么就通过/index 这个路径来访问。
    这个类还有两点需要注意:
    1. /login 只支持get方式。 post方式是后续用来进行登录行为的,这里的get方式仅仅用于显示登录页面
    2. 权限注解:
    通过注解: @RequiresRoles("productManager") 指明了 访问 deleteProduct 需要角色"productManager"
    通过注解:@RequiresPermissions("deleteOrder") 指明了 访问 deleteOrder 需要权限"deleteOrder"
     1 /**
     2  * 专门用于显示页面的控制器
     3  */
     4 @Controller
     5 @RequestMapping("")
     6 public class PageController {
     7 
     8     @RequestMapping("/index")
     9     public String index() {
    10         return "index";
    11     }
    12 
    13     @RequiresRoles("productManager")
    14     @RequestMapping("/deleteProduct")
    15     public String deleteProduct() {
    16         return "deleteProduct";
    17     }
    18 
    19     @RequestMapping("/listProduct")
    20     public String listProduct(){
    21         return "listProduct";
    22     }
    23 
    24     @RequiresPermissions("deleteOrder")
    25     @RequestMapping("/deleteOrder")
    26     public String deleteOrder() {
    27         return "deleteOrder";
    28     }
    29     @RequestMapping(value = "/login",method = RequestMethod.GET)
    30     public String login() {
    31         return "login";
    32     }
    33 
    34     @RequestMapping("/unauthorized")
    35     public String noperms() {
    36         return "unauthorized";
    37     }
    38 }
    View Code
    8.jsp
    index.jsp, listProduct.jsp,deleteOrder.jsp,login.jsp,unauthorized.jsp
     
    9.User相关
    User.java,UserMapper.java,UserMapper.xml,UserService.java,UserServiceImpl.java
     
    10.Role相关
    Role.java,RoleMapper.java,RoleMapper.xml,RoleService.java,RoleServiceImpl.java
     
    10.Permission相关
    Permission.java,PermissionMapper.java,PermissionMapper.xml,PermissionService.java,PermissionServiceImpl.java
     
    11.LoginController.java
     1 @Controller
     2 public class LoginController {
     3 
     4     /**
     5      * 登录方法
     6      * @param name
     7      * @param password
     8      * @return
     9      */
    10     @RequestMapping(value = "/login", method = RequestMethod.POST)
    11     public String login(String name, String password, Model model) {
    12         UsernamePasswordToken token = new UsernamePasswordToken(name, password);
    13         Subject subject = SecurityUtils.getSubject();
    14         try {
    15             subject.login(token);
    16             Session session = subject.getSession();
    17             session.setAttribute("subject", subject);
    18             return "redirect:index";
    19         } catch (AuthenticationException e) {
    20             //认证失败
    21             model.addAttribute("error", "登录失败");
    22             return "login";
    23         }
    24     }
    25 }
    View Code
    12.DefaultExceptionHandler.java
    最后是异常处理,当发生UnauthorizedException 异常的时候,就表示访问了无授权的资源,那么就会跳转到unauthorized.jsp,而在unauthorized.jsp 中就会把错误信息通过变量 ex 取出来。
     
    DefaultExceptionHandler 的使用,是声明在 springMVC.xml 的最后几行
    /**
     * 对异常进行统一的处理
     */
    @ControllerAdvice
    public class DefaultExceptionHandler {
        @ExceptionHandler({UnauthorizedException.class})
        @ResponseStatus(HttpStatus.UNAUTHORIZED)
        public ModelAndView processUnauthenticatedExcetpion(NativeWebRequest request, UnauthorizedException e) {
            ModelAndView mv = new ModelAndView();
            mv.addObject("ex", e);
            mv.setViewName("unauthorized");
            return mv;
        }
    }
     
  • 相关阅读:
    Nature:肿瘤转移后的基因组特征
    Nature | 生物体可以从头产生新基因
    一文读懂:DNA甲基化的作用及各种高通量检测方法比较
    Nature | 新技术scSLAM-seq可在单细胞水平揭示转录动态变化的核心特征
    一文读懂长非编码RNA(lncRNA)的分类、功能及测序鉴定方法
    Science重磅 | 新技术Slide-seq能以高空间分辨率测量全基因组的表达情况
    Science综述 | 用单细胞基因组学将人类细胞表型匹配到基因型
    Nature Methods | 新软件SAVER-X可对单细胞转录组学数据进行有效降噪
    Circular RNA的产生机制、功能及RNA-seq数据鉴定方法
    一文搞懂基因融合(gene fusion)的定义、产生机制及鉴定方法
  • 原文地址:https://www.cnblogs.com/blogforvi/p/11670400.html
Copyright © 2020-2023  润新知