首页 > 代码库 > 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>
(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 }
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 }
(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 }
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 }
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 }
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 }
(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 }
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 }
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 }
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 }
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 }
通过 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 }
(5)Controller
om.github.zhangkaitao.shiro.chapter17.web.controller 包下的 IndexController、LoginController、UserController 和 ClientController,其用于维护后端的数据,如用户及客户端数据;即相当于后台管理。
1 @Controller 2 public class IndexController { 3 4 @RequestMapping("/") 5 public String index(Model model) { 6 return "index"; 7 } 8 }
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 }
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 }
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 }
授权控制器: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>
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>
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>
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>
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>
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>
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
(7)服务器的维护
访问 localhost:8080/chapter17-server/,登录后进行客户端管理和用户管理。
客户端管理就是进行客户端的注册,如新浪微博的第三方应用就需要到新浪微博开发平台进行注册;
用户管理就是进行如新浪微博用户的管理。
3.客户端
客户端流程可以参照如很多网站的新浪微博登录功能,或其他的第三方帐号登录功能。
2017.2.16 开涛shiro教程-第十七章-OAuth2集成