首页 > 代码库 > 2017.2.16 开涛shiro教程-第十七章-OAuth2集成

2017.2.16 开涛shiro教程-第十七章-OAuth2集成

原博客地址:http://jinnianshilongnian.iteye.com/blog/2018398

根据下载的pdf学习。

 

开涛shiro教程-第十七章-OAuth2集成

1.OAuth2介绍

(1)应用场景

很多开放平台,比如新浪微博开放平台,都在使用开发API接口供开发者使用。即带来了,第三方应用要到开放平台授权的问题。OAuth就是做这个的。

1 OAuth2官网:http://oauth.net/2/
2 OAuth2协议:http://tools.ietf.org/html/rfc6749
3 本文使用:Apache Oltu
4 使用文档:https://cwiki.apache.org/confluence/display/OLTU/Documentation 

 

(2)OAuth角色

1 资源拥有者resource owner:能授权访问受保护资源的一个实体。比如新浪微博用户lyh。
2 资源服务器resource server:存储受保护资源。
3 授权服务器authorization server:成功验证resource owner,并获取授权,颁发授权令牌access token给客户端client。
4 客户端client:本身不存储资源,而是resource owner授权通过后,使用access token访问受保护资源,然后把相应的数据展示/提交到服务器。

 

(3)OAuth2协议流程

技术分享

 

2.服务器端

(1)POM依赖

此处我们使用 apache oltu oauth2 服务端实现,需要引入 authzserver(授权服务器依赖)和 resourceserver(资源服务器依赖)。

 1        <dependency>
 2             <groupId>org.apache.oltu.oauth2</groupId>
 3             <artifactId>org.apache.oltu.oauth2.authzserver</artifactId>
 4             <version>0.31</version>
 5         </dependency>
 6 
 7         <dependency>
 8             <groupId>org.apache.oltu.oauth2</groupId>
 9             <artifactId>org.apache.oltu.oauth2.resourceserver</artifactId>
10             <version>0.31</version>
11         </dependency>

附完整pom.xml文件:

技术分享
  1 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  2          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  3     <parent>
  4         <artifactId>shiro-example</artifactId>
  5         <groupId>com.github.zhangkaitao</groupId>
  6         <version>1.0-SNAPSHOT</version>
  7     </parent>
  8     <modelVersion>4.0.0</modelVersion>
  9     <artifactId>shiro-example-chapter17-server</artifactId>
 10     <packaging>war</packaging>
 11     <name>shiro-example-chapter17-server</name>
 12     <url>http://maven.apache.org</url>
 13     <dependencies>
 14         <dependency>
 15             <groupId>junit</groupId>
 16             <artifactId>junit</artifactId>
 17             <version>3.8.1</version>
 18             <scope>test</scope>
 19         </dependency>
 20 
 21         <dependency>
 22             <groupId>commons-collections</groupId>
 23             <artifactId>commons-collections</artifactId>
 24             <version>3.2.1</version>
 25         </dependency>
 26 
 27         <dependency>
 28             <groupId>org.apache.oltu.oauth2</groupId>
 29             <artifactId>org.apache.oltu.oauth2.authzserver</artifactId>
 30             <version>0.31</version>
 31         </dependency>
 32 
 33         <dependency>
 34             <groupId>org.apache.oltu.oauth2</groupId>
 35             <artifactId>org.apache.oltu.oauth2.resourceserver</artifactId>
 36             <version>0.31</version>
 37         </dependency>
 38 
 39 
 40         <dependency>
 41             <groupId>javax.servlet</groupId>
 42             <artifactId>javax.servlet-api</artifactId>
 43             <version>3.0.1</version>
 44             <scope>provided</scope>
 45         </dependency>
 46         <dependency>
 47             <groupId>javax.servlet.jsp</groupId>
 48             <artifactId>jsp-api</artifactId>
 49             <version>2.2</version>
 50         </dependency>
 51         <dependency>
 52             <groupId>javax.servlet</groupId>
 53             <artifactId>jstl</artifactId>
 54             <version>1.2</version>
 55         </dependency>
 56 
 57 
 58         <dependency>
 59             <groupId>org.apache.shiro</groupId>
 60             <artifactId>shiro-core</artifactId>
 61             <version>1.2.2</version>
 62         </dependency>
 63         <dependency>
 64             <groupId>org.apache.shiro</groupId>
 65             <artifactId>shiro-ehcache</artifactId>
 66             <version>1.2.2</version>
 67         </dependency>
 68         <dependency>
 69             <groupId>org.apache.shiro</groupId>
 70             <artifactId>shiro-web</artifactId>
 71             <version>1.2.2</version>
 72         </dependency>
 73         <dependency>
 74             <groupId>org.apache.shiro</groupId>
 75             <artifactId>shiro-quartz</artifactId>
 76             <version>1.2.2</version>
 77         </dependency>
 78         <dependency>
 79             <groupId>org.apache.shiro</groupId>
 80             <artifactId>shiro-spring</artifactId>
 81             <version>1.2.2</version>
 82         </dependency>
 83 
 84 
 85         <dependency>
 86             <groupId>mysql</groupId>
 87             <artifactId>mysql-connector-java</artifactId>
 88             <version>5.1.25</version>
 89         </dependency>
 90         <dependency>
 91             <groupId>com.alibaba</groupId>
 92             <artifactId>druid</artifactId>
 93             <version>0.2.23</version>
 94         </dependency>
 95 
 96 
 97         <!-- aspectj相关jar包-->
 98         <dependency>
 99             <groupId>org.aspectj</groupId>
100             <artifactId>aspectjrt</artifactId>
101             <version>1.7.4</version>
102         </dependency>
103         <dependency>
104             <groupId>org.aspectj</groupId>
105             <artifactId>aspectjweaver</artifactId>
106             <version>1.7.4</version>
107         </dependency>
108 
109         <dependency>
110             <groupId>org.springframework</groupId>
111             <artifactId>spring-context-support</artifactId>
112             <version>4.0.0.RELEASE</version>
113         </dependency>
114 
115         <dependency>
116             <groupId>org.springframework</groupId>
117             <artifactId>spring-jdbc</artifactId>
118             <version>4.0.0.RELEASE</version>
119         </dependency>
120 
121         <dependency>
122             <groupId>org.springframework</groupId>
123             <artifactId>spring-tx</artifactId>
124             <version>4.0.0.RELEASE</version>
125         </dependency>
126 
127         <dependency>
128             <groupId>org.springframework</groupId>
129             <artifactId>spring-webmvc</artifactId>
130             <version>4.0.0.RELEASE</version>
131         </dependency>
132 
133         <!--jackson -->
134         <dependency>
135             <groupId>com.fasterxml.jackson.core</groupId>
136             <artifactId>jackson-databind</artifactId>
137             <version>2.2.3</version>
138         </dependency>
139 
140     </dependencies>
141     <build>
142         <finalName>chapter17-server</finalName>
143         <plugins>
144             <plugin>
145                 <groupId>org.mortbay.jetty</groupId>
146                 <artifactId>jetty-maven-plugin</artifactId>
147                 <version>8.1.8.v20121106</version>
148                 <configuration>
149                     <webAppConfig>
150                         <contextPath>/${project.build.finalName}</contextPath>
151                     </webAppConfig>
152                 </configuration>
153             </plugin>
154 
155 
156             <plugin>
157                 <groupId>org.apache.tomcat.maven</groupId>
158                 <artifactId>tomcat7-maven-plugin</artifactId>
159                 <version>2.2</version>
160                 <configuration>
161                     <path>/${project.build.finalName}</path>
162                 </configuration>
163 
164             </plugin>
165         </plugins>
166 
167 
168     </build>
169 </project>
pom.xml

 

(2)table

shiro-schema.sql

oauth2_user存储着resource owner,oauth2_client存储着client的信息,在进行授权时使用。

 1 drop table if exists oauth2_client;
 2 drop table if exists oauth2_user;
 3 
 4 create table oauth2_user (
 5   id bigint auto_increment,
 6   username varchar(100),
 7   password varchar(100),
 8   salt varchar(100),
 9   constraint pk_oauth2_user primary key(id)
10 ) charset=utf8 ENGINE=InnoDB;
11 create unique index idx_oauth2_user_username on oauth2_user(username);
12 
13 create table oauth2_client (
14   id bigint auto_increment,
15   client_name varchar(100),
16   client_id varchar(100),
17   client_secret varchar(100),
18   constraint pk_oauth2_client primary key(id)
19 ) charset=utf8 ENGINE=InnoDB;
20 create index idx_oauth2_client_client_id on oauth2_client(client_id);

 

shiro-data.sql:

DELIMITER ;
delete from oauth2_user;
delete from oauth2_client;

insert into oauth2_user values(1,‘admin‘,‘d3c59d25033dbf980d29554025c23a75‘,‘8d78869f470951332959580424d4bf4f‘);
insert into oauth2_client values(1,‘chapter17-client‘,‘c1ebe466-1cdc-4bd3-ab69-77c3561b9dee‘,‘d8346ea2-6017-43ed-ad68-19c0f971738b‘);

 

(2)entity

技术分享
 1 package com.github.zhangkaitao.shiro.chapter17.entity;
 2 
 3 import java.io.Serializable;
 4 
 5 /**
 6  * <p>User: Zhang Kaitao
 7  * <p>Date: 14-2-17
 8  * <p>Version: 1.0
 9  */
10 public class Client implements Serializable {
11 
12     private Long id;
13     private String clientName;
14     private String clientId;
15     private String clientSecret;
16 
17     public Long getId() {
18         return id;
19     }
20 
21     public void setId(Long id) {
22         this.id = id;
23     }
24 
25     public String getClientName() {
26         return clientName;
27     }
28 
29     public void setClientName(String clientName) {
30         this.clientName = clientName;
31     }
32 
33     public String getClientId() {
34         return clientId;
35     }
36 
37     public void setClientId(String clientId) {
38         this.clientId = clientId;
39     }
40 
41     public String getClientSecret() {
42         return clientSecret;
43     }
44 
45     public void setClientSecret(String clientSecret) {
46         this.clientSecret = clientSecret;
47     }
48 
49     @Override
50     public boolean equals(Object o) {
51         if (this == o) return true;
52         if (o == null || getClass() != o.getClass()) return false;
53 
54         Client client = (Client) o;
55 
56         if (id != null ? !id.equals(client.id) : client.id != null) return false;
57 
58         return true;
59     }
60 
61     @Override
62     public int hashCode() {
63         return id != null ? id.hashCode() : 0;
64     }
65 
66     @Override
67     public String toString() {
68         return "Client{" +
69                 "id=" + id +
70                 ", clientName=‘" + clientName + ‘\‘‘ +
71                 ", clientId=‘" + clientId + ‘\‘‘ +
72                 ", clientSecret=‘" + clientSecret + ‘\‘‘ +
73                 ‘}‘;
74     }
75 }
Client
技术分享
 1 package com.github.zhangkaitao.shiro.chapter17.entity;
 2 
 3 import java.io.Serializable;
 4 
 5 /**
 6  * <p>User: Zhang Kaitao
 7  * <p>Date: 14-2-17
 8  * <p>Version: 1.0
 9  */
10 public class User implements Serializable {
11     private Long id; //编号
12     private String username; //用户名
13     private String password; //密码
14     private String salt; //加密密码的盐
15 
16     public Long getId() {
17         return id;
18     }
19 
20     public void setId(Long id) {
21         this.id = id;
22     }
23 
24     public String getUsername() {
25         return username;
26     }
27 
28     public void setUsername(String username) {
29         this.username = username;
30     }
31 
32     public String getPassword() {
33         return password;
34     }
35 
36     public void setPassword(String password) {
37         this.password = password;
38     }
39 
40     public String getSalt() {
41         return salt;
42     }
43 
44     public void setSalt(String salt) {
45         this.salt = salt;
46     }
47 
48     public String getCredentialsSalt() {
49         return username + salt;
50     }
51 
52 
53     @Override
54     public boolean equals(Object o) {
55         if (this == o) return true;
56         if (o == null || getClass() != o.getClass()) return false;
57 
58         User user = (User) o;
59 
60         if (id != null ? !id.equals(user.id) : user.id != null) return false;
61 
62         return true;
63     }
64 
65     @Override
66     public int hashCode() {
67         return id != null ? id.hashCode() : 0;
68     }
69 
70     @Override
71     public String toString() {
72         return "User{" +
73                 "id=" + id +
74                 ", username=‘" + username + ‘\‘‘ +
75                 ", password=‘" + password + ‘\‘‘ +
76                 ", salt=‘" + salt + ‘\‘‘ +
77                 ‘}‘;
78     }
79 }
User

 

(3)dao

技术分享
 1 public interface ClientDao {
 2 
 3     public Client createClient(Client client);
 4     public Client updateClient(Client client);
 5     public void deleteClient(Long clientId);
 6 
 7     Client findOne(Long clientId);
 8 
 9     List<Client> findAll();
10 
11     Client findByClientId(String clientId);
12     Client findByClientSecret(String clientSecret);
13 
14 }
ClientDao
技术分享
 1 package com.github.zhangkaitao.shiro.chapter17.dao;
 2 
 3 import com.github.zhangkaitao.shiro.chapter17.entity.Client;
 4 import org.springframework.beans.factory.annotation.Autowired;
 5 import org.springframework.jdbc.core.BeanPropertyRowMapper;
 6 import org.springframework.jdbc.core.JdbcTemplate;
 7 import org.springframework.jdbc.core.PreparedStatementCreator;
 8 import org.springframework.jdbc.support.GeneratedKeyHolder;
 9 import org.springframework.stereotype.Repository;
10 
11 import java.sql.Connection;
12 import java.sql.PreparedStatement;
13 import java.sql.SQLException;
14 import java.util.List;
15 
16 /**
17  * <p>User: Zhang Kaitao
18  * <p>Date: 14-1-28
19  * <p>Version: 1.0
20  */
21 @Repository
22 public class ClientDaoImpl implements ClientDao {
23 
24     @Autowired
25     private JdbcTemplate jdbcTemplate;
26     
27     public Client createClient(final Client client) {
28         final String sql = "insert into oauth2_client(client_name, client_id, client_secret) values(?,?,?)";
29 
30         GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
31         jdbcTemplate.update(new PreparedStatementCreator() {
32             @Override
33             public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
34                 PreparedStatement psst = connection.prepareStatement(sql, new String[]{"id"});
35                 int count = 1;
36                 psst.setString(count++, client.getClientName());
37                 psst.setString(count++, client.getClientId());
38                 psst.setString(count++, client.getClientSecret());
39                 return psst;
40             }
41         }, keyHolder);
42 
43         client.setId(keyHolder.getKey().longValue());
44         return client;
45     }
46 
47     public Client updateClient(Client client) {
48         String sql = "update oauth2_client set client_name=?, client_id=?, client_secret=? where id=?";
49         jdbcTemplate.update(
50                 sql,
51                 client.getClientName(), client.getClientId(), client.getClientSecret(), client.getId());
52         return client;
53     }
54 
55     public void deleteClient(Long clientId) {
56         String sql = "delete from oauth2_client where id=?";
57         jdbcTemplate.update(sql, clientId);
58     }
59 
60     @Override
61     public Client findOne(Long clientId) {
62         String sql = "select id, client_name, client_id, client_secret from oauth2_client where id=?";
63         List<Client> clientList = jdbcTemplate.query(sql, new BeanPropertyRowMapper(Client.class), clientId);
64         if(clientList.size() == 0) {
65             return null;
66         }
67         return clientList.get(0);
68     }
69 
70     @Override
71     public List<Client> findAll() {
72         String sql = "select id, client_name, client_id, client_secret from oauth2_client";
73         return jdbcTemplate.query(sql, new BeanPropertyRowMapper(Client.class));
74     }
75 
76 
77     @Override
78     public Client findByClientId(String clientId) {
79         String sql = "select id, client_name, client_id, client_secret from oauth2_client where client_id=?";
80         List<Client> clientList = jdbcTemplate.query(sql, new BeanPropertyRowMapper(Client.class), clientId);
81         if(clientList.size() == 0) {
82             return null;
83         }
84         return clientList.get(0);
85     }
86 
87 
88     @Override
89     public Client findByClientSecret(String clientSecret) {
90         String sql = "select id, client_name, client_id, client_secret from oauth2_client where client_secret=?";
91         List<Client> clientList = jdbcTemplate.query(sql, new BeanPropertyRowMapper(Client.class), clientSecret);
92         if(clientList.size() == 0) {
93             return null;
94         }
95         return clientList.get(0);
96     }
97 }
ClientDaoImpl
技术分享
 1 public interface UserDao {
 2 
 3     public User createUser(User user);
 4     public User updateUser(User user);
 5     public void deleteUser(Long userId);
 6 
 7     User findOne(Long userId);
 8 
 9     List<User> findAll();
10 
11     User findByUsername(String username);
12 
13 }
UserDao
技术分享
 1 package com.github.zhangkaitao.shiro.chapter17.dao;
 2 
 3 import com.github.zhangkaitao.shiro.chapter17.entity.User;
 4 import org.springframework.beans.factory.annotation.Autowired;
 5 import org.springframework.jdbc.core.BeanPropertyRowMapper;
 6 import org.springframework.jdbc.core.JdbcTemplate;
 7 import org.springframework.jdbc.core.PreparedStatementCreator;
 8 import org.springframework.jdbc.support.GeneratedKeyHolder;
 9 import org.springframework.stereotype.Repository;
10 
11 import java.sql.Connection;
12 import java.sql.PreparedStatement;
13 import java.sql.SQLException;
14 import java.util.List;
15 
16 @Repository
17 public class UserDaoImpl implements UserDao {
18 
19     @Autowired
20     private JdbcTemplate jdbcTemplate;
21     
22     public User createUser(final User user) {
23         final String sql = "insert into oauth2_user(username, password, salt) values(?,?,?)";
24 
25         GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
26         jdbcTemplate.update(new PreparedStatementCreator() {
27             @Override
28             public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
29                 PreparedStatement psst = connection.prepareStatement(sql, new String[]{"id"});
30                 int count = 1;
31                 psst.setString(count++, user.getUsername());
32                 psst.setString(count++, user.getPassword());
33                 psst.setString(count++, user.getSalt());
34                 return psst;
35             }
36         }, keyHolder);
37 
38         user.setId(keyHolder.getKey().longValue());
39         return user;
40     }
41 
42     public User updateUser(User user) {
43         String sql = "update oauth2_user set username=?, password=?, salt=? where id=?";
44         jdbcTemplate.update(
45                 sql,
46                 user.getUsername(), user.getPassword(), user.getSalt(), user.getId());
47         return user;
48     }
49 
50     public void deleteUser(Long userId) {
51         String sql = "delete from oauth2_user where id=?";
52         jdbcTemplate.update(sql, userId);
53     }
54 
55     @Override
56     public User findOne(Long userId) {
57         String sql = "select id, username, password, salt from oauth2_user where id=?";
58         List<User> userList = jdbcTemplate.query(sql, new BeanPropertyRowMapper(User.class), userId);
59         if(userList.size() == 0) {
60             return null;
61         }
62         return userList.get(0);
63     }
64 
65     @Override
66     public List<User> findAll() {
67         String sql = "select id, username, password, salt from oauth2_user";
68         return jdbcTemplate.query(sql, new BeanPropertyRowMapper(User.class));
69     }
70 
71 
72     @Override
73     public User findByUsername(String username) {
74         String sql = "select id, username, password, salt from oauth2_user where username=?";
75         List<User> userList = jdbcTemplate.query(sql, new BeanPropertyRowMapper(User.class), username);
76         if(userList.size() == 0) {
77             return null;
78         }
79         return userList.get(0);
80     }
81 }
UserDaoImpl

 

(4)service

技术分享
 1 public interface ClientService {
 2 
 3     public Client createClient(Client client);
 4     public Client updateClient(Client client);
 5     public void deleteClient(Long clientId);
 6 
 7     Client findOne(Long clientId);
 8 
 9     List<Client> findAll();
10 
11     Client findByClientId(String clientId);
12     Client findByClientSecret(String clientSecret);
13 
14 }
ClientService
技术分享
 1 @Transactional
 2 @Service
 3 public class ClientServiceImpl implements ClientService {
 4     @Autowired
 5     private ClientDao clientDao;
 6 
 7     @Override
 8     public Client createClient(Client client) {
 9 
10         client.setClientId(UUID.randomUUID().toString());
11         client.setClientSecret(UUID.randomUUID().toString());
12         return clientDao.createClient(client);
13     }
14 
15     @Override
16     public Client updateClient(Client client) {
17         return clientDao.updateClient(client);
18     }
19 
20     @Override
21     public void deleteClient(Long clientId) {
22         clientDao.deleteClient(clientId);
23     }
24 
25     @Override
26     public Client findOne(Long clientId) {
27         return clientDao.findOne(clientId);
28     }
29 
30     @Override
31     public List<Client> findAll() {
32         return clientDao.findAll();
33     }
34 
35     @Override
36     public Client findByClientId(String clientId) {
37         return clientDao.findByClientId(clientId);
38     }
39 
40     @Override
41     public Client findByClientSecret(String clientSecret) {
42         return clientDao.findByClientSecret(clientSecret);
43     }
44 }
ClientServiceImpl
技术分享
 1 public interface UserService {
 2    
 3     public User createUser(User user);
 4     public User updateUser(User user);
 5     public void deleteUser(Long userId);
 6 
 7     public void changePassword(Long userId, String newPassword);
 8 
 9     User findOne(Long userId);
10     List<User> findAll();
11     public User findByUsername(String username);
12 
13 }
UserService
技术分享
 1 @Transactional
 2 @Service
 3 public class UserServiceImpl implements UserService {
 4     @Autowired
 5     private UserDao userDao;
 6     @Autowired
 7     private PasswordHelper passwordHelper;
 8 
 9     public User createUser(User user) {
10         //加密密码
11         passwordHelper.encryptPassword(user);
12         return userDao.createUser(user);
13     }
14 
15     @Override
16     public User updateUser(User user) {
17         return userDao.updateUser(user);
18     }
19 
20     @Override
21     public void deleteUser(Long userId) {
22         userDao.deleteUser(userId);
23     }
24 
25     public void changePassword(Long userId, String newPassword) {
26         User user =userDao.findOne(userId);
27         user.setPassword(newPassword);
28         passwordHelper.encryptPassword(user);
29         userDao.updateUser(user);
30     }
31 
32     @Override
33     public User findOne(Long userId) {
34         return userDao.findOne(userId);
35     }
36 
37     @Override
38     public List<User> findAll() {
39         return userDao.findAll();
40     }
41 
42     public User findByUsername(String username) {
43         return userDao.findByUsername(username);
44     }
45 
46 
47 }
UserServiceImpl
技术分享
 1 @Service
 2 public class PasswordHelper {
 3 
 4     private RandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator();
 5 
 6     @Value("${password.algorithmName}")
 7     private String algorithmName = "md5";
 8     @Value("${password.hashIterations}")
 9     private int hashIterations = 2;
10 
11     public void setRandomNumberGenerator(RandomNumberGenerator randomNumberGenerator) {
12         this.randomNumberGenerator = randomNumberGenerator;
13     }
14 
15     public void setAlgorithmName(String algorithmName) {
16         this.algorithmName = algorithmName;
17     }
18 
19     public void setHashIterations(int hashIterations) {
20         this.hashIterations = hashIterations;
21     }
22 
23     public void encryptPassword(User user) {
24 
25         user.setSalt(randomNumberGenerator.nextBytes().toHex());
26 
27         String newPassword = new SimpleHash(
28                 algorithmName,
29                 user.getPassword(),
30                 ByteSource.Util.bytes(user.getCredentialsSalt()),
31                 hashIterations).toHex();
32 
33         user.setPassword(newPassword);
34     }
35 }
PasswordHelper

 

通过 OAuthService 实现进行 auth code access token 的维护。

 1 public interface OAuthService {
 2 
 3     //添加 auth code
 4     public void addAuthCode(String authCode, String username);
 5     //添加 access token
 6     public void addAccessToken(String accessToken, String username);
 7 
 8     //验证auth code是否有效
 9     boolean checkAuthCode(String authCode);
10     //验证access token是否有效
11     boolean checkAccessToken(String accessToken);
12 
13     String getUsernameByAuthCode(String authCode);
14     String getUsernameByAccessToken(String accessToken);
15 
16     //auth code / access token 过期时间
17     long getExpireIn();
18 
19     public boolean checkClientId(String clientId);
20     public boolean checkClientSecret(String clientSecret);
21 
22 }
技术分享
 1 @Service
 2 public class OAuthServiceImpl implements OAuthService {
 3 
 4     private Cache cache;
 5 
 6     @Autowired
 7     private ClientService clientService;
 8 
 9     @Autowired
10     public OAuthServiceImpl(CacheManager cacheManager) {
11         this.cache = cacheManager.getCache("code-cache");
12     }
13 
14     @Override
15     public void addAuthCode(String authCode, String username) {
16         cache.put(authCode, username);
17     }
18 
19     @Override
20     public void addAccessToken(String accessToken, String username) {
21         cache.put(accessToken, username);
22     }
23 
24     @Override
25     public String getUsernameByAuthCode(String authCode) {
26         return (String)cache.get(authCode).get();
27     }
28 
29     @Override
30     public String getUsernameByAccessToken(String accessToken) {
31         return (String)cache.get(accessToken).get();
32     }
33 
34     @Override
35     public boolean checkAuthCode(String authCode) {
36         return cache.get(authCode) != null;
37     }
38 
39     @Override
40     public boolean checkAccessToken(String accessToken) {
41         return cache.get(accessToken) != null;
42     }
43 
44     @Override
45     public boolean checkClientId(String clientId) {
46         return clientService.findByClientId(clientId) != null;
47     }
48 
49     @Override
50     public boolean checkClientSecret(String clientSecret) {
51         return clientService.findByClientSecret(clientSecret) != null;
52     }
53 
54     @Override
55     public long getExpireIn() {
56         return 3600L;
57     }
58 }
OAuthServiceImpl

 

(5)Controller

om.github.zhangkaitao.shiro.chapter17.web.controller 包下的 IndexControllerLoginControllerUserController ClientController,其用于维护后端的数据,如用户及客户端数据;即相当于后台管理。

技术分享
1 @Controller
2 public class IndexController {
3 
4     @RequestMapping("/")
5     public String index(Model model) {
6         return "index";
7     }
8 }
IndexController
技术分享
 1 @Controller
 2 public class LoginController {
 3 
 4     @RequestMapping(value = "http://www.mamicode.com/login")
 5     public String showLoginForm(HttpServletRequest req, Model model) {
 6         String exceptionClassName = (String)req.getAttribute("shiroLoginFailure");
 7         String error = null;
 8         if(UnknownAccountException.class.getName().equals(exceptionClassName)) {
 9             error = "用户名/密码错误";
10         } else if(IncorrectCredentialsException.class.getName().equals(exceptionClassName)) {
11             error = "用户名/密码错误";
12         } else if(exceptionClassName != null) {
13             error = "其他错误:" + exceptionClassName;
14         }
15         model.addAttribute("error", error);
16         return "login";
17     }
18 }
LoginController
技术分享
 1 @Controller
 2 @RequestMapping("/user")
 3 public class UserController {
 4 
 5     @Autowired
 6     private UserService userService;
 7 
 8     @RequestMapping(method = RequestMethod.GET)
 9     public String list(Model model) {
10         model.addAttribute("userList", userService.findAll());
11         return "user/list";
12     }
13 
14     @RequestMapping(value = "http://www.mamicode.com/create", method = RequestMethod.GET)
15     public String showCreateForm(Model model) {
16         model.addAttribute("user", new User());
17         model.addAttribute("op", "新增");
18         return "user/edit";
19     }
20 
21     @RequestMapping(value = "http://www.mamicode.com/create", method = RequestMethod.POST)
22     public String create(User user, RedirectAttributes redirectAttributes) {
23         userService.createUser(user);
24         redirectAttributes.addFlashAttribute("msg", "新增成功");
25         return "redirect:/user";
26     }
27 
28     @RequestMapping(value = "http://www.mamicode.com/{id}/update", method = RequestMethod.GET)
29     public String showUpdateForm(@PathVariable("id") Long id, Model model) {
30         model.addAttribute("user", userService.findOne(id));
31         model.addAttribute("op", "修改");
32         return "user/edit";
33     }
34 
35     @RequestMapping(value = "http://www.mamicode.com/{id}/update", method = RequestMethod.POST)
36     public String update(User user, RedirectAttributes redirectAttributes) {
37         userService.updateUser(user);
38         redirectAttributes.addFlashAttribute("msg", "修改成功");
39         return "redirect:/user";
40     }
41 
42     @RequestMapping(value = "http://www.mamicode.com/{id}/delete", method = RequestMethod.GET)
43     public String showDeleteForm(@PathVariable("id") Long id, Model model) {
44         model.addAttribute("user", userService.findOne(id));
45         model.addAttribute("op", "删除");
46         return "user/edit";
47     }
48 
49     @RequestMapping(value = "http://www.mamicode.com/{id}/delete", method = RequestMethod.POST)
50     public String delete(@PathVariable("id") Long id, RedirectAttributes redirectAttributes) {
51         userService.deleteUser(id);
52         redirectAttributes.addFlashAttribute("msg", "删除成功");
53         return "redirect:/user";
54     }
55 
56 
57     @RequestMapping(value = "http://www.mamicode.com/{id}/changePassword", method = RequestMethod.GET)
58     public String showChangePasswordForm(@PathVariable("id") Long id, Model model) {
59         model.addAttribute("user", userService.findOne(id));
60         model.addAttribute("op", "修改密码");
61         return "user/changePassword";
62     }
63 
64     @RequestMapping(value = "http://www.mamicode.com/{id}/changePassword", method = RequestMethod.POST)
65     public String changePassword(@PathVariable("id") Long id, String newPassword, RedirectAttributes redirectAttributes) {
66         userService.changePassword(id, newPassword);
67         redirectAttributes.addFlashAttribute("msg", "修改密码成功");
68         return "redirect:/user";
69     }
70 
71 }
UserController
技术分享
 1 @Controller
 2 @RequestMapping("/client")
 3 public class ClientController {
 4 
 5     @Autowired
 6     private ClientService clientService;
 7 
 8     @RequestMapping(method = RequestMethod.GET)
 9     public String list(Model model) {
10         model.addAttribute("clientList", clientService.findAll());
11         return "client/list";
12     }
13 
14     @RequestMapping(value = "http://www.mamicode.com/create", method = RequestMethod.GET)
15     public String showCreateForm(Model model) {
16         model.addAttribute("client", new Client());
17         model.addAttribute("op", "新增");
18         return "client/edit";
19     }
20 
21     @RequestMapping(value = "http://www.mamicode.com/create", method = RequestMethod.POST)
22     public String create(Client client, RedirectAttributes redirectAttributes) {
23         clientService.createClient(client);
24         redirectAttributes.addFlashAttribute("msg", "新增成功");
25         return "redirect:/client";
26     }
27 
28     @RequestMapping(value = "http://www.mamicode.com/{id}/update", method = RequestMethod.GET)
29     public String showUpdateForm(@PathVariable("id") Long id, Model model) {
30         model.addAttribute("client", clientService.findOne(id));
31         model.addAttribute("op", "修改");
32         return "client/edit";
33     }
34 
35     @RequestMapping(value = "http://www.mamicode.com/{id}/update", method = RequestMethod.POST)
36     public String update(Client client, RedirectAttributes redirectAttributes) {
37         clientService.updateClient(client);
38         redirectAttributes.addFlashAttribute("msg", "修改成功");
39         return "redirect:/client";
40     }
41 
42     @RequestMapping(value = "http://www.mamicode.com/{id}/delete", method = RequestMethod.GET)
43     public String showDeleteForm(@PathVariable("id") Long id, Model model) {
44         model.addAttribute("client", clientService.findOne(id));
45         model.addAttribute("op", "删除");
46         return "client/edit";
47     }
48 
49     @RequestMapping(value = "http://www.mamicode.com/{id}/delete", method = RequestMethod.POST)
50     public String delete(@PathVariable("id") Long id, RedirectAttributes redirectAttributes) {
51         clientService.deleteClient(id);
52         redirectAttributes.addFlashAttribute("msg", "删除成功");
53         return "redirect:/client";
54     }
55 
56 }
ClientController

 

授权控制器:AuthorizeController

  1 @Controller
  2 public class AuthorizeController {
  3 
  4     @Autowired
  5     private OAuthService oAuthService;
  6     @Autowired
  7     private ClientService clientService;
  8 
  9     @RequestMapping("/authorize")
 10     public Object authorize(
 11             Model model,
 12             HttpServletRequest request)
 13             throws URISyntaxException, OAuthSystemException {
 14 
 15         try {
 16             //构建OAuth 授权请求
 17             OAuthAuthzRequest oauthRequest = new OAuthAuthzRequest(request);
 18 
 19             //检查传入的客户端id是否正确
 20             if (!oAuthService.checkClientId(oauthRequest.getClientId())) {
 21                 OAuthResponse response =
 22                         OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST)
 23                                 .setError(OAuthError.TokenResponse.INVALID_CLIENT)
 24                                 .setErrorDescription(Constants.INVALID_CLIENT_DESCRIPTION)
 25                                 .buildJSONMessage();
 26                 return new ResponseEntity(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
 27             }
 28 
 29 
 30             Subject subject = SecurityUtils.getSubject();
 31             //如果用户没有登录,跳转到登陆页面
 32             if(!subject.isAuthenticated()) {
 33                 if(!login(subject, request)) {//登录失败时跳转到登陆页面
 34                     model.addAttribute("client", clientService.findByClientId(oauthRequest.getClientId()));
 35                     return "oauth2login";
 36                 }
 37             }
 38 
 39             String username = (String)subject.getPrincipal();
 40             //生成授权码
 41             String authorizationCode = null;
 42             //responseType目前仅支持CODE,另外还有TOKEN
 43             String responseType = oauthRequest.getParam(OAuth.OAUTH_RESPONSE_TYPE);
 44             if (responseType.equals(ResponseType.CODE.toString())) {
 45                 OAuthIssuerImpl oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());
 46                 authorizationCode = oauthIssuerImpl.authorizationCode();
 47                 oAuthService.addAuthCode(authorizationCode, username);
 48             }
 49 
 50             //进行OAuth响应构建
 51             OAuthASResponse.OAuthAuthorizationResponseBuilder builder =
 52                     OAuthASResponse.authorizationResponse(request, HttpServletResponse.SC_FOUND);
 53             //设置授权码
 54             builder.setCode(authorizationCode);
 55             //得到到客户端重定向地址
 56             String redirectURI = oauthRequest.getParam(OAuth.OAUTH_REDIRECT_URI);
 57 
 58             //构建响应
 59             final OAuthResponse response = builder.location(redirectURI).buildQueryMessage();
 60 
 61             //根据OAuthResponse返回ResponseEntity响应
 62             HttpHeaders headers = new HttpHeaders();
 63             headers.setLocation(new URI(response.getLocationUri()));
 64             return new ResponseEntity(headers, HttpStatus.valueOf(response.getResponseStatus()));
 65         } catch (OAuthProblemException e) {
 66 
 67             //出错处理
 68             String redirectUri = e.getRedirectUri();
 69             if (OAuthUtils.isEmpty(redirectUri)) {
 70                 //告诉客户端没有传入redirectUri直接报错
 71                 return new ResponseEntity("OAuth callback url needs to be provided by client!!!", HttpStatus.NOT_FOUND);
 72             }
 73 
 74             //返回错误消息(如?error=)
 75             final OAuthResponse response =
 76                     OAuthASResponse.errorResponse(HttpServletResponse.SC_FOUND)
 77                             .error(e).location(redirectUri).buildQueryMessage();
 78             HttpHeaders headers = new HttpHeaders();
 79             headers.setLocation(new URI(response.getLocationUri()));
 80             return new ResponseEntity(headers, HttpStatus.valueOf(response.getResponseStatus()));
 81         }
 82     }
 83 
 84     private boolean login(Subject subject, HttpServletRequest request) {
 85         if("get".equalsIgnoreCase(request.getMethod())) {
 86             return false;
 87         }
 88         String username = request.getParameter("username");
 89         String password = request.getParameter("password");
 90 
 91         if(StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
 92             return false;
 93         }
 94 
 95         UsernamePasswordToken token = new UsernamePasswordToken(username, password);
 96 
 97         try {
 98             subject.login(token);
 99             return true;
100         } catch (Exception e) {
101             request.setAttribute("error", "登录失败:" + e.getClass().getName());
102             return false;
103         }
104     }
105 }

 

访问令牌控制器:AccessTokenController

 1 @RestController
 2 public class AccessTokenController {
 3 
 4     @Autowired
 5     private OAuthService oAuthService;
 6 
 7     @Autowired
 8     private UserService userService;
 9 
10     @RequestMapping("/accessToken")
11     public HttpEntity token(HttpServletRequest request)
12             throws URISyntaxException, OAuthSystemException {
13 
14         try {
15             //构建OAuth请求
16             OAuthTokenRequest oauthRequest = new OAuthTokenRequest(request);
17 
18             //检查提交的客户端id是否正确
19             if (!oAuthService.checkClientId(oauthRequest.getClientId())) {
20                 OAuthResponse response =
21                         OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST)
22                                 .setError(OAuthError.TokenResponse.INVALID_CLIENT)
23                                 .setErrorDescription(Constants.INVALID_CLIENT_DESCRIPTION)
24                                 .buildJSONMessage();
25                 return new ResponseEntity(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
26             }
27 
28             // 检查客户端安全KEY是否正确
29             if (!oAuthService.checkClientSecret(oauthRequest.getClientSecret())) {
30                 OAuthResponse response =
31                         OAuthASResponse.errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
32                                 .setError(OAuthError.TokenResponse.UNAUTHORIZED_CLIENT)
33                                 .setErrorDescription(Constants.INVALID_CLIENT_DESCRIPTION)
34                                 .buildJSONMessage();
35                 return new ResponseEntity(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
36             }
37 
38             String authCode = oauthRequest.getParam(OAuth.OAUTH_CODE);
39             // 检查验证类型,此处只检查AUTHORIZATION_CODE类型,其他的还有PASSWORD或REFRESH_TOKEN
40             if (oauthRequest.getParam(OAuth.OAUTH_GRANT_TYPE).equals(GrantType.AUTHORIZATION_CODE.toString())) {
41                 if (!oAuthService.checkAuthCode(authCode)) {
42                     OAuthResponse response = OAuthASResponse
43                             .errorResponse(HttpServletResponse.SC_BAD_REQUEST)
44                             .setError(OAuthError.TokenResponse.INVALID_GRANT)
45                             .setErrorDescription("错误的授权码")
46                             .buildJSONMessage();
47                     return new ResponseEntity(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
48                 }
49             }
50 
51             //生成Access Token
52             OAuthIssuer oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());
53             final String accessToken = oauthIssuerImpl.accessToken();
54             oAuthService.addAccessToken(accessToken, oAuthService.getUsernameByAuthCode(authCode));
55 
56 
57             //生成OAuth响应
58             OAuthResponse response = OAuthASResponse
59                     .tokenResponse(HttpServletResponse.SC_OK)
60                     .setAccessToken(accessToken)
61                     .setExpiresIn(String.valueOf(oAuthService.getExpireIn()))
62                     .buildJSONMessage();
63 
64             //根据OAuthResponse生成ResponseEntity
65             return new ResponseEntity(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
66 
67         } catch (OAuthProblemException e) {
68             //构建错误响应
69             OAuthResponse res = OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST).error(e)
70                     .buildJSONMessage();
71             return new ResponseEntity(res.getBody(), HttpStatus.valueOf(res.getResponseStatus()));
72         }
73     }
74 
75 }

 

资源控制器:UserInfoController

 1 @RestController
 2 public class UserInfoController {
 3 
 4     @Autowired
 5     private OAuthService oAuthService;
 6 
 7     @RequestMapping("/userInfo")
 8     public HttpEntity userInfo(HttpServletRequest request) throws OAuthSystemException {
 9         try {
10 
11             //构建OAuth资源请求
12             OAuthAccessResourceRequest oauthRequest = new OAuthAccessResourceRequest(request, ParameterStyle.QUERY);
13             //获取Access Token
14             String accessToken = oauthRequest.getAccessToken();
15 
16             //验证Access Token
17             if (!oAuthService.checkAccessToken(accessToken)) {
18                 // 如果不存在/过期了,返回未验证错误,需重新验证
19                 OAuthResponse oauthResponse = OAuthRSResponse
20                         .errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
21                         .setRealm(Constants.RESOURCE_SERVER_NAME)
22                         .setError(OAuthError.ResourceResponse.INVALID_TOKEN)
23                         .buildHeaderMessage();
24 
25                 HttpHeaders headers = new HttpHeaders();
26                 headers.add(OAuth.HeaderType.WWW_AUTHENTICATE, oauthResponse.getHeader(OAuth.HeaderType.WWW_AUTHENTICATE));
27                 return new ResponseEntity(headers, HttpStatus.UNAUTHORIZED);
28             }
29             //返回用户名
30             String username = oAuthService.getUsernameByAccessToken(accessToken);
31             return new ResponseEntity(username, HttpStatus.OK);
32         } catch (OAuthProblemException e) {
33             //检查是否设置了错误码
34             String errorCode = e.getError();
35             if (OAuthUtils.isEmpty(errorCode)) {
36                 OAuthResponse oauthResponse = OAuthRSResponse
37                         .errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
38                         .setRealm(Constants.RESOURCE_SERVER_NAME)
39                         .buildHeaderMessage();
40 
41                 HttpHeaders headers = new HttpHeaders();
42                 headers.add(OAuth.HeaderType.WWW_AUTHENTICATE, oauthResponse.getHeader(OAuth.HeaderType.WWW_AUTHENTICATE));
43                 return new ResponseEntity(headers, HttpStatus.UNAUTHORIZED);
44             }
45 
46             OAuthResponse oauthResponse = OAuthRSResponse
47                     .errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
48                     .setRealm(Constants.RESOURCE_SERVER_NAME)
49                     .setError(e.getError())
50                     .setErrorDescription(e.getDescription())
51                     .setErrorUri(e.getUri())
52                     .buildHeaderMessage();
53 
54             HttpHeaders headers = new HttpHeaders();
55             headers.add(OAuth.HeaderType.WWW_AUTHENTICATE, oauthResponse.getHeader(OAuth.HeaderType.WWW_AUTHENTICATE));
56             return new ResponseEntity(HttpStatus.BAD_REQUEST);
57         }
58     }
59 }

 

(6)配置文件

技术分享
 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:aop="http://www.springframework.org/schema/aop"
 5        xmlns:tx="http://www.springframework.org/schema/tx"
 6        xmlns:context="http://www.springframework.org/schema/context"
 7        xsi:schemaLocation="
 8           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
 9           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
10           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
11           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
12 
13     <context:property-placeholder location="classpath:resources.properties"/>
14 
15     <!-- 扫描注解Bean -->
16     <context:component-scan base-package="com.github.zhangkaitao.shiro.chapter17">
17         <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
18     </context:component-scan>
19 
20     <!-- 开启AOP监听 只对当前配置文件有效 -->
21     <aop:aspectj-autoproxy expose-proxy="true"/>
22 
23 
24     <!-- 数据源 -->
25     <!--see https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_DruidDataSource%E5%8F%82%E8%80%83%E9%85%8D%E7%BD%AE-->
26     <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
27         <!-- 基本属性 url、user、password -->
28         <property name="url" value="${connection.url}"/>
29         <property name="username" value="${connection.username}"/>
30         <property name="password" value="${connection.password}"/>
31 
32         <!-- 配置初始化大小、最小、最大 -->
33         <property name="initialSize" value="${druid.initialSize}"/>
34         <property name="minIdle" value="${druid.minIdle}"/>
35         <property name="maxActive" value="${druid.maxActive}"/>
36 
37         <!-- 配置获取连接等待超时的时间 -->
38         <property name="maxWait" value="${druid.maxWait}"/>
39         <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
40         <property name="timeBetweenEvictionRunsMillis" value="${druid.timeBetweenEvictionRunsMillis}" />
41 
42         <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
43         <property name="minEvictableIdleTimeMillis" value="${druid.minEvictableIdleTimeMillis}" />
44 
45         <property name="validationQuery" value="${druid.validationQuery}" />
46         <property name="testWhileIdle" value="${druid.testWhileIdle}" />
47         <property name="testOnBorrow" value="${druid.testOnBorrow}" />
48         <property name="testOnReturn" value="${druid.testOnReturn}" />
49 
50         <!-- 打开PSCache,并且指定每个连接上PSCache的大小  如果用Oracle,则把poolPreparedStatements配置为true,mysql可以配置为false。-->
51         <property name="poolPreparedStatements" value="${druid.poolPreparedStatements}" />
52         <property name="maxPoolPreparedStatementPerConnectionSize" value="${druid.maxPoolPreparedStatementPerConnectionSize}" />
53 
54         <!-- 配置监控统计拦截的filters -->
55         <property name="filters" value="${druid.filters}" />
56 
57     </bean>
58 
59     <bean id="dataSourceProxy" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
60         <property name="targetDataSource" ref="dataSource"/>
61     </bean>
62 
63     <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
64         <constructor-arg ref="dataSourceProxy"/>
65     </bean>
66 
67     <!--事务管理器配置-->
68     <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
69         <property name="dataSource" ref="dataSourceProxy"/>
70     </bean>
71 
72     <tx:advice id="txAdvice" transaction-manager="transactionManager">
73         <tx:attributes>
74             <tx:method name="*" propagation="REQUIRED"/>
75         </tx:attributes>
76     </tx:advice>
77 
78     <aop:config expose-proxy="true" proxy-target-class="true">
79         <!-- 只对业务逻辑层实施事务 -->
80         <aop:pointcut id="txPointcut" expression="execution(* com.github.zhangkaitao.shiro.chapter16..service..*+.*(..))"/>
81         <aop:advisor id="txAdvisor" advice-ref="txAdvice" pointcut-ref="txPointcut"/>
82     </aop:config>
83 
84     <import resource="classpath:spring-config-cache.xml"/>
85     <import resource="classpath:spring-config-shiro.xml"/>
86 </beans>
spring-config.xml
技术分享
 1 <beans xmlns="http://www.springframework.org/schema/beans"
 2        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3        xmlns:p="http://www.springframework.org/schema/p"
 4        xmlns:context="http://www.springframework.org/schema/context"
 5        xmlns:mvc="http://www.springframework.org/schema/mvc"
 6        xsi:schemaLocation="
 7        http://www.springframework.org/schema/beans
 8        http://www.springframework.org/schema/beans/spring-beans.xsd
 9        http://www.springframework.org/schema/context
10        http://www.springframework.org/schema/context/spring-context.xsd
11        http://www.springframework.org/schema/mvc
12        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
13 
14     <context:property-placeholder location="classpath:resources.properties"/>
15 
16     <!-- 开启controller注解支持 -->
17     <!-- 注意事项请参考:http://jinnianshilongnian.iteye.com/blog/1762632 -->
18     <context:component-scan base-package="com.github.zhangkaitao.**.web.controller" use-default-filters="false">
19         <context:include-filter type="annotation"
20                                 expression="org.springframework.stereotype.Controller"/>
21         <context:include-filter type="annotation"
22                                 expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
23     </context:component-scan>
24 
25 
26     <mvc:annotation-driven>
27     </mvc:annotation-driven>
28 
29     <!-- 当在web.xml 中   DispatcherServlet使用 <url-pattern>/</url-pattern> 映射时,能映射静态资源 -->
30     <mvc:default-servlet-handler/>
31 
32     <!-- 静态资源映射 -->
33     <mvc:resources mapping="/static/**" location="/WEB-INF/static/"/>
34 
35 
36     <!-- 默认的视图解析器 在上边的解析错误时使用 (默认使用html)- -->
37     <bean id="defaultViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"
38           p:order="1">
39         <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
40         <property name="contentType" value="text/html"/>
41         <property name="prefix" value="/WEB-INF/jsp/"/>
42         <property name="suffix" value=".jsp"/>
43     </bean>
44 
45     <!-- 控制器异常处理 -->
46     <bean id="exceptionHandlerExceptionResolver" class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver">
47     </bean>
48 
49     <bean class="com.github.zhangkaitao.shiro.chapter17.web.exception.DefaultExceptionHandler"/>
50 
51     <import resource="spring-mvc-shiro.xml"/>
52 
53 </beans>
spring-mvc.xml
技术分享
 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:util="http://www.springframework.org/schema/util"
 4        xmlns:aop="http://www.springframework.org/schema/aop"
 5        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 6        xsi:schemaLocation="
 7        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
 8        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
 9        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
10 
11     <aop:config proxy-target-class="true"></aop:config>
12     <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
13         <property name="securityManager" ref="securityManager"/>
14     </bean>
15 
16 </beans>
spring-mvc-shiro.xml
技术分享
  1 <?xml version="1.0" encoding="UTF-8"?>
  2 <beans xmlns="http://www.springframework.org/schema/beans"
  3        xmlns:util="http://www.springframework.org/schema/util"
  4        xmlns:aop="http://www.springframework.org/schema/aop"
  5        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  6        xsi:schemaLocation="
  7        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  8        http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
  9        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
 10 
 11     <!-- 缓存管理器 -->
 12     <bean id="cacheManager" class="com.github.zhangkaitao.shiro.spring.SpringCacheManagerWrapper">
 13         <property name="cacheManager" ref="springCacheManager"/>
 14     </bean>
 15 
 16     <!-- 凭证匹配器 -->
 17     <bean id="credentialsMatcher" class="com.github.zhangkaitao.shiro.chapter17.credentials.RetryLimitHashedCredentialsMatcher">
 18         <constructor-arg ref="cacheManager"/>
 19         <property name="hashAlgorithmName" value="md5"/>
 20         <property name="hashIterations" value="2"/>
 21         <property name="storedCredentialsHexEncoded" value="true"/>
 22     </bean>
 23 
 24     <!-- Realm实现 -->
 25     <bean id="userRealm" class="com.github.zhangkaitao.shiro.chapter17.realm.UserRealm">
 26         <property name="credentialsMatcher" ref="credentialsMatcher"/>
 27         <property name="cachingEnabled" value="false"/>
 28         <!--<property name="authenticationCachingEnabled" value="http://www.mamicode.com/true"/>-->
 29         <!--<property name="authenticationCacheName" value="http://www.mamicode.com/authenticationCache"/>-->
 30         <!--<property name="authorizationCachingEnabled" value="http://www.mamicode.com/true"/>-->
 31         <!--<property name="authorizationCacheName" value="http://www.mamicode.com/authorizationCache"/>-->
 32     </bean>
 33 
 34     <!-- 会话ID生成器 -->
 35     <bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/>
 36 
 37     <!-- 会话Cookie模板 -->
 38     <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
 39         <constructor-arg value="sid"/>
 40         <property name="httpOnly" value="true"/>
 41         <property name="maxAge" value="-1"/>
 42     </bean>
 43 
 44     <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
 45         <constructor-arg value="rememberMe"/>
 46         <property name="httpOnly" value="true"/>
 47         <property name="maxAge" value="2592000"/><!-- 30天 -->
 48     </bean>
 49 
 50     <!-- rememberMe管理器 -->
 51     <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
 52         <!-- rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)-->
 53         <property name="cipherKey"
 54                   value="#{T(org.apache.shiro.codec.Base64).decode(‘4AvVhmFLUs0KTA3Kprsdag==‘)}"/>
 55         <property name="cookie" ref="rememberMeCookie"/>
 56     </bean>
 57 
 58     <!-- 会话DAO -->
 59     <bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
 60         <property name="activeSessionsCacheName" value="shiro-activeSessionCache"/>
 61         <property name="sessionIdGenerator" ref="sessionIdGenerator"/>
 62     </bean>
 63 
 64     <!-- 会话验证调度器 -->
 65     <bean id="sessionValidationScheduler" class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler">
 66         <property name="sessionValidationInterval" value="1800000"/>
 67         <property name="sessionManager" ref="sessionManager"/>
 68     </bean>
 69 
 70     <!-- 会话管理器 -->
 71     <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
 72         <property name="globalSessionTimeout" value="1800000"/>
 73         <property name="deleteInvalidSessions" value="true"/>
 74         <property name="sessionValidationSchedulerEnabled" value="true"/>
 75         <property name="sessionValidationScheduler" ref="sessionValidationScheduler"/>
 76         <property name="sessionDAO" ref="sessionDAO"/>
 77         <property name="sessionIdCookieEnabled" value="true"/>
 78         <property name="sessionIdCookie" ref="sessionIdCookie"/>
 79     </bean>
 80 
 81     <!-- 安全管理器 -->
 82     <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
 83         <property name="realm" ref="userRealm"/>
 84         <property name="sessionManager" ref="sessionManager"/>
 85         <property name="cacheManager" ref="cacheManager"/>
 86         <property name="rememberMeManager" ref="rememberMeManager"/>
 87     </bean>
 88 
 89     <!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) -->
 90     <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
 91         <property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
 92         <property name="arguments" ref="securityManager"/>
 93     </bean>
 94 
 95     <!-- 基于Form表单的身份验证过滤器 -->
 96     <bean id="formAuthenticationFilter" class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter">
 97         <property name="usernameParam" value="username"/>
 98         <property name="passwordParam" value="password"/>
 99         <property name="rememberMeParam" value="rememberMe"/>
100         <property name="loginUrl" value="/login"/>
101     </bean>
102 
103     <!-- Shiro的Web过滤器 -->
104     <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
105         <property name="securityManager" ref="securityManager"/>
106         <property name="loginUrl" value="/login"/>
107         <property name="filters">
108             <util:map>
109                 <entry key="authc" value-ref="formAuthenticationFilter"/>
110             </util:map>
111         </property>
112         <property name="filterChainDefinitions">
113             <value>
114                 / = anon
115                 /login = authc
116                 /logout = logout
117 
118                 /authorize=anon
119                 /accessToken=anon
120                 /userInfo=anon
121 
122                 /** = user
123             </value>
124         </property>
125     </bean>
126 
127     <!-- Shiro生命周期处理器-->
128     <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
129 
130 </beans>
spring-config-shiro.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        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
 5 
 6     <bean id="springCacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
 7         <property name="cacheManager" ref="ehcacheManager"/>
 8     </bean>
 9 
10     <!--ehcache-->
11     <bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
12         <property name="configLocation" value="classpath:ehcache/ehcache.xml"/>
13     </bean>
14 
15 
16 </beans>
spring-config-cache.xml
技术分享
 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <ehcache name="es">
 3 
 4     <diskStore path="java.io.tmpdir"/>
 5 
 6     <!-- 登录记录缓存 锁定10分钟 -->
 7     <cache name="passwordRetryCache"
 8            maxEntriesLocalHeap="2000"
 9            eternal="false"
10            timeToIdleSeconds="3600"
11            timeToLiveSeconds="0"
12            overflowToDisk="false"
13            statistics="true">
14     </cache>
15 
16     <cache name="authorizationCache"
17            maxEntriesLocalHeap="2000"
18            eternal="false"
19            timeToIdleSeconds="3600"
20            timeToLiveSeconds="0"
21            overflowToDisk="false"
22            statistics="true">
23     </cache>
24 
25     <cache name="authenticationCache"
26            maxEntriesLocalHeap="2000"
27            eternal="false"
28            timeToIdleSeconds="3600"
29            timeToLiveSeconds="0"
30            overflowToDisk="false"
31            statistics="true">
32     </cache>
33 
34     <cache name="shiro-activeSessionCache"
35            maxEntriesLocalHeap="2000"
36            eternal="false"
37            timeToIdleSeconds="3600"
38            timeToLiveSeconds="0"
39            overflowToDisk="false"
40            statistics="true">
41     </cache>
42 
43     <cache name="code-cache"
44            maxEntriesLocalHeap="2000"
45            eternal="false"
46            timeToIdleSeconds="3600"
47            timeToLiveSeconds="0"
48            overflowToDisk="false"
49            statistics="true">
50     </cache>
51 
52 </ehcache>
ehcache.xml
技术分享
 1 #dataSource configure
 2 connection.url=jdbc:mysql://localhost:3306/shiro-oauth2
 3 connection.username=root
 4 connection.password=
 5 
 6 #druid datasource
 7 #参考 https://github.com/alibaba/druid/wiki/%E9%85%8D%E7%BD%AE_DruidDataSource%E5%8F%82%E8%80%83%E9%85%8D%E7%BD%AE
 8 druid.initialSize=10
 9 druid.minIdle=10
10 druid.maxActive=50
11 druid.maxWait=60000
12 druid.timeBetweenEvictionRunsMillis=60000
13 druid.minEvictableIdleTimeMillis=300000
14 druid.validationQuery=SELECT ‘x‘
15 druid.testWhileIdle=true
16 druid.testOnBorrow=false
17 druid.testOnReturn=false
18 druid.poolPreparedStatements=true
19 druid.maxPoolPreparedStatementPerConnectionSize=20
20 druid.filters=wall,stat
21 
22 #shiro
23 password.algorithmName=md5
24 password.hashIterations=2
resource.properties

 

(7)服务器的维护

访问 localhost:8080/chapter17-server/,登录后进行客户端管理和用户管理。

客户端管理就是进行客户端的注册,如新浪微博的第三方应用就需要到新浪微博开发平台进行注册;

用户管理就是进行如新浪微博用户的管理。


3.客户端

客户端流程可以参照如很多网站的新浪微博登录功能,或其他的第三方帐号登录功能。


 

2017.2.16 开涛shiro教程-第十七章-OAuth2集成