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 ,结果:\n%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;
    }
}
 
02-13 21:09