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

根据下载的pdf学习。

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

1.OAuth2介绍

(1)应用场景

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

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

(2)OAuth角色

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

(3)OAuth2协议流程

2.服务器端

(1)POM依赖

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

        <dependency>
<groupId>org.apache.oltu.oauth2</groupId>
<artifactId>org.apache.oltu.oauth2.authzserver</artifactId>
<version>0.31</version>
</dependency> <dependency>
<groupId>org.apache.oltu.oauth2</groupId>
<artifactId>org.apache.oltu.oauth2.resourceserver</artifactId>
<version>0.31</version>
</dependency>

附完整pom.xml文件:

 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>shiro-example</artifactId>
<groupId>com.github.zhangkaitao</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>shiro-example-chapter17-server</artifactId>
<packaging>war</packaging>
<name>shiro-example-chapter17-server</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency> <dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.2.1</version>
</dependency> <dependency>
<groupId>org.apache.oltu.oauth2</groupId>
<artifactId>org.apache.oltu.oauth2.authzserver</artifactId>
<version>0.31</version>
</dependency> <dependency>
<groupId>org.apache.oltu.oauth2</groupId>
<artifactId>org.apache.oltu.oauth2.resourceserver</artifactId>
<version>0.31</version>
</dependency> <dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency> <dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-quartz</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.2.2</version>
</dependency> <dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.25</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>0.2.23</version>
</dependency> <!-- aspectj相关jar包-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.7.4</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.7.4</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.0.0.RELEASE</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.0.0.RELEASE</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.0.0.RELEASE</version>
</dependency> <dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.0.0.RELEASE</version>
</dependency> <!--jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.2.3</version>
</dependency> </dependencies>
<build>
<finalName>chapter17-server</finalName>
<plugins>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>8.1.8.v20121106</version>
<configuration>
<webAppConfig>
<contextPath>/${project.build.finalName}</contextPath>
</webAppConfig>
</configuration>
</plugin> <plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<path>/${project.build.finalName}</path>
</configuration> </plugin>
</plugins> </build>
</project>

pom.xml

(2)table

shiro-schema.sql

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

 drop table if exists oauth2_client;
drop table if exists oauth2_user; create table oauth2_user (
id bigint auto_increment,
username varchar(100),
password varchar(100),
salt varchar(100),
constraint pk_oauth2_user primary key(id)
) charset=utf8 ENGINE=InnoDB;
create unique index idx_oauth2_user_username on oauth2_user(username); create table oauth2_client (
id bigint auto_increment,
client_name varchar(100),
client_id varchar(100),
client_secret varchar(100),
constraint pk_oauth2_client primary key(id)
) charset=utf8 ENGINE=InnoDB;
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

 package com.github.zhangkaitao.shiro.chapter17.entity;

 import java.io.Serializable;

 /**
* <p>User: Zhang Kaitao
* <p>Date: 14-2-17
* <p>Version: 1.0
*/
public class Client implements Serializable { private Long id;
private String clientName;
private String clientId;
private String clientSecret; public Long getId() {
return id;
} public void setId(Long id) {
this.id = id;
} public String getClientName() {
return clientName;
} public void setClientName(String clientName) {
this.clientName = clientName;
} public String getClientId() {
return clientId;
} public void setClientId(String clientId) {
this.clientId = clientId;
} public String getClientSecret() {
return clientSecret;
} public void setClientSecret(String clientSecret) {
this.clientSecret = clientSecret;
} @Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; Client client = (Client) o; if (id != null ? !id.equals(client.id) : client.id != null) return false; return true;
} @Override
public int hashCode() {
return id != null ? id.hashCode() : 0;
} @Override
public String toString() {
return "Client{" +
"id=" + id +
", clientName='" + clientName + '\'' +
", clientId='" + clientId + '\'' +
", clientSecret='" + clientSecret + '\'' +
'}';
}
}

Client

 package com.github.zhangkaitao.shiro.chapter17.entity;

 import java.io.Serializable;

 /**
* <p>User: Zhang Kaitao
* <p>Date: 14-2-17
* <p>Version: 1.0
*/
public class User implements Serializable {
private Long id; //编号
private String username; //用户名
private String password; //密码
private String salt; //加密密码的盐 public Long getId() {
return id;
} public void setId(Long id) {
this.id = id;
} public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
} public String getSalt() {
return salt;
} public void setSalt(String salt) {
this.salt = salt;
} public String getCredentialsSalt() {
return username + salt;
} @Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false; User user = (User) o; if (id != null ? !id.equals(user.id) : user.id != null) return false; return true;
} @Override
public int hashCode() {
return id != null ? id.hashCode() : 0;
} @Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", salt='" + salt + '\'' +
'}';
}
}

User

(3)dao

 public interface ClientDao {

     public Client createClient(Client client);
public Client updateClient(Client client);
public void deleteClient(Long clientId); Client findOne(Long clientId); List<Client> findAll(); Client findByClientId(String clientId);
Client findByClientSecret(String clientSecret); }

ClientDao

 package com.github.zhangkaitao.shiro.chapter17.dao;

 import com.github.zhangkaitao.shiro.chapter17.entity.Client;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.stereotype.Repository; import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List; /**
* <p>User: Zhang Kaitao
* <p>Date: 14-1-28
* <p>Version: 1.0
*/
@Repository
public class ClientDaoImpl implements ClientDao { @Autowired
private JdbcTemplate jdbcTemplate; public Client createClient(final Client client) {
final String sql = "insert into oauth2_client(client_name, client_id, client_secret) values(?,?,?)"; GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
PreparedStatement psst = connection.prepareStatement(sql, new String[]{"id"});
int count = 1;
psst.setString(count++, client.getClientName());
psst.setString(count++, client.getClientId());
psst.setString(count++, client.getClientSecret());
return psst;
}
}, keyHolder); client.setId(keyHolder.getKey().longValue());
return client;
} public Client updateClient(Client client) {
String sql = "update oauth2_client set client_name=?, client_id=?, client_secret=? where id=?";
jdbcTemplate.update(
sql,
client.getClientName(), client.getClientId(), client.getClientSecret(), client.getId());
return client;
} public void deleteClient(Long clientId) {
String sql = "delete from oauth2_client where id=?";
jdbcTemplate.update(sql, clientId);
} @Override
public Client findOne(Long clientId) {
String sql = "select id, client_name, client_id, client_secret from oauth2_client where id=?";
List<Client> clientList = jdbcTemplate.query(sql, new BeanPropertyRowMapper(Client.class), clientId);
if(clientList.size() == 0) {
return null;
}
return clientList.get(0);
} @Override
public List<Client> findAll() {
String sql = "select id, client_name, client_id, client_secret from oauth2_client";
return jdbcTemplate.query(sql, new BeanPropertyRowMapper(Client.class));
} @Override
public Client findByClientId(String clientId) {
String sql = "select id, client_name, client_id, client_secret from oauth2_client where client_id=?";
List<Client> clientList = jdbcTemplate.query(sql, new BeanPropertyRowMapper(Client.class), clientId);
if(clientList.size() == 0) {
return null;
}
return clientList.get(0);
} @Override
public Client findByClientSecret(String clientSecret) {
String sql = "select id, client_name, client_id, client_secret from oauth2_client where client_secret=?";
List<Client> clientList = jdbcTemplate.query(sql, new BeanPropertyRowMapper(Client.class), clientSecret);
if(clientList.size() == 0) {
return null;
}
return clientList.get(0);
}
}

ClientDaoImpl

 public interface UserDao {

     public User createUser(User user);
public User updateUser(User user);
public void deleteUser(Long userId); User findOne(Long userId); List<User> findAll(); User findByUsername(String username); }

UserDao

 package com.github.zhangkaitao.shiro.chapter17.dao;

 import com.github.zhangkaitao.shiro.chapter17.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementCreator;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.stereotype.Repository; import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.List; @Repository
public class UserDaoImpl implements UserDao { @Autowired
private JdbcTemplate jdbcTemplate; public User createUser(final User user) {
final String sql = "insert into oauth2_user(username, password, salt) values(?,?,?)"; GeneratedKeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(new PreparedStatementCreator() {
@Override
public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
PreparedStatement psst = connection.prepareStatement(sql, new String[]{"id"});
int count = 1;
psst.setString(count++, user.getUsername());
psst.setString(count++, user.getPassword());
psst.setString(count++, user.getSalt());
return psst;
}
}, keyHolder); user.setId(keyHolder.getKey().longValue());
return user;
} public User updateUser(User user) {
String sql = "update oauth2_user set username=?, password=?, salt=? where id=?";
jdbcTemplate.update(
sql,
user.getUsername(), user.getPassword(), user.getSalt(), user.getId());
return user;
} public void deleteUser(Long userId) {
String sql = "delete from oauth2_user where id=?";
jdbcTemplate.update(sql, userId);
} @Override
public User findOne(Long userId) {
String sql = "select id, username, password, salt from oauth2_user where id=?";
List<User> userList = jdbcTemplate.query(sql, new BeanPropertyRowMapper(User.class), userId);
if(userList.size() == 0) {
return null;
}
return userList.get(0);
} @Override
public List<User> findAll() {
String sql = "select id, username, password, salt from oauth2_user";
return jdbcTemplate.query(sql, new BeanPropertyRowMapper(User.class));
} @Override
public User findByUsername(String username) {
String sql = "select id, username, password, salt from oauth2_user where username=?";
List<User> userList = jdbcTemplate.query(sql, new BeanPropertyRowMapper(User.class), username);
if(userList.size() == 0) {
return null;
}
return userList.get(0);
}
}

UserDaoImpl

(4)service

 public interface ClientService {

     public Client createClient(Client client);
public Client updateClient(Client client);
public void deleteClient(Long clientId); Client findOne(Long clientId); List<Client> findAll(); Client findByClientId(String clientId);
Client findByClientSecret(String clientSecret); }

ClientService

 @Transactional
@Service
public class ClientServiceImpl implements ClientService {
@Autowired
private ClientDao clientDao; @Override
public Client createClient(Client client) { client.setClientId(UUID.randomUUID().toString());
client.setClientSecret(UUID.randomUUID().toString());
return clientDao.createClient(client);
} @Override
public Client updateClient(Client client) {
return clientDao.updateClient(client);
} @Override
public void deleteClient(Long clientId) {
clientDao.deleteClient(clientId);
} @Override
public Client findOne(Long clientId) {
return clientDao.findOne(clientId);
} @Override
public List<Client> findAll() {
return clientDao.findAll();
} @Override
public Client findByClientId(String clientId) {
return clientDao.findByClientId(clientId);
} @Override
public Client findByClientSecret(String clientSecret) {
return clientDao.findByClientSecret(clientSecret);
}
}

ClientServiceImpl

 public interface UserService {

     public User createUser(User user);
public User updateUser(User user);
public void deleteUser(Long userId); public void changePassword(Long userId, String newPassword); User findOne(Long userId);
List<User> findAll();
public User findByUsername(String username); }

UserService

 @Transactional
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserDao userDao;
@Autowired
private PasswordHelper passwordHelper; public User createUser(User user) {
//加密密码
passwordHelper.encryptPassword(user);
return userDao.createUser(user);
} @Override
public User updateUser(User user) {
return userDao.updateUser(user);
} @Override
public void deleteUser(Long userId) {
userDao.deleteUser(userId);
} public void changePassword(Long userId, String newPassword) {
User user =userDao.findOne(userId);
user.setPassword(newPassword);
passwordHelper.encryptPassword(user);
userDao.updateUser(user);
} @Override
public User findOne(Long userId) {
return userDao.findOne(userId);
} @Override
public List<User> findAll() {
return userDao.findAll();
} public User findByUsername(String username) {
return userDao.findByUsername(username);
} }

UserServiceImpl

 @Service
public class PasswordHelper { private RandomNumberGenerator randomNumberGenerator = new SecureRandomNumberGenerator(); @Value("${password.algorithmName}")
private String algorithmName = "md5";
@Value("${password.hashIterations}")
private int hashIterations = 2; public void setRandomNumberGenerator(RandomNumberGenerator randomNumberGenerator) {
this.randomNumberGenerator = randomNumberGenerator;
} public void setAlgorithmName(String algorithmName) {
this.algorithmName = algorithmName;
} public void setHashIterations(int hashIterations) {
this.hashIterations = hashIterations;
} public void encryptPassword(User user) { user.setSalt(randomNumberGenerator.nextBytes().toHex()); String newPassword = new SimpleHash(
algorithmName,
user.getPassword(),
ByteSource.Util.bytes(user.getCredentialsSalt()),
hashIterations).toHex(); user.setPassword(newPassword);
}
}

PasswordHelper

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

 public interface OAuthService {

     //添加 auth code
public void addAuthCode(String authCode, String username);
//添加 access token
public void addAccessToken(String accessToken, String username); //验证auth code是否有效
boolean checkAuthCode(String authCode);
//验证access token是否有效
boolean checkAccessToken(String accessToken); String getUsernameByAuthCode(String authCode);
String getUsernameByAccessToken(String accessToken); //auth code / access token 过期时间
long getExpireIn(); public boolean checkClientId(String clientId);
public boolean checkClientSecret(String clientSecret); }
 @Service
public class OAuthServiceImpl implements OAuthService { private Cache cache; @Autowired
private ClientService clientService; @Autowired
public OAuthServiceImpl(CacheManager cacheManager) {
this.cache = cacheManager.getCache("code-cache");
} @Override
public void addAuthCode(String authCode, String username) {
cache.put(authCode, username);
} @Override
public void addAccessToken(String accessToken, String username) {
cache.put(accessToken, username);
} @Override
public String getUsernameByAuthCode(String authCode) {
return (String)cache.get(authCode).get();
} @Override
public String getUsernameByAccessToken(String accessToken) {
return (String)cache.get(accessToken).get();
} @Override
public boolean checkAuthCode(String authCode) {
return cache.get(authCode) != null;
} @Override
public boolean checkAccessToken(String accessToken) {
return cache.get(accessToken) != null;
} @Override
public boolean checkClientId(String clientId) {
return clientService.findByClientId(clientId) != null;
} @Override
public boolean checkClientSecret(String clientSecret) {
return clientService.findByClientSecret(clientSecret) != null;
} @Override
public long getExpireIn() {
return 3600L;
}
}

OAuthServiceImpl

(5)Controller

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

 @Controller
public class IndexController { @RequestMapping("/")
public String index(Model model) {
return "index";
}
}

IndexController

 @Controller
public class LoginController { @RequestMapping(value = "/login")
public String showLoginForm(HttpServletRequest req, Model model) {
String exceptionClassName = (String)req.getAttribute("shiroLoginFailure");
String error = null;
if(UnknownAccountException.class.getName().equals(exceptionClassName)) {
error = "用户名/密码错误";
} else if(IncorrectCredentialsException.class.getName().equals(exceptionClassName)) {
error = "用户名/密码错误";
} else if(exceptionClassName != null) {
error = "其他错误:" + exceptionClassName;
}
model.addAttribute("error", error);
return "login";
}
}

LoginController

 @Controller
@RequestMapping("/user")
public class UserController { @Autowired
private UserService userService; @RequestMapping(method = RequestMethod.GET)
public String list(Model model) {
model.addAttribute("userList", userService.findAll());
return "user/list";
} @RequestMapping(value = "/create", method = RequestMethod.GET)
public String showCreateForm(Model model) {
model.addAttribute("user", new User());
model.addAttribute("op", "新增");
return "user/edit";
} @RequestMapping(value = "/create", method = RequestMethod.POST)
public String create(User user, RedirectAttributes redirectAttributes) {
userService.createUser(user);
redirectAttributes.addFlashAttribute("msg", "新增成功");
return "redirect:/user";
} @RequestMapping(value = "/{id}/update", method = RequestMethod.GET)
public String showUpdateForm(@PathVariable("id") Long id, Model model) {
model.addAttribute("user", userService.findOne(id));
model.addAttribute("op", "修改");
return "user/edit";
} @RequestMapping(value = "/{id}/update", method = RequestMethod.POST)
public String update(User user, RedirectAttributes redirectAttributes) {
userService.updateUser(user);
redirectAttributes.addFlashAttribute("msg", "修改成功");
return "redirect:/user";
} @RequestMapping(value = "/{id}/delete", method = RequestMethod.GET)
public String showDeleteForm(@PathVariable("id") Long id, Model model) {
model.addAttribute("user", userService.findOne(id));
model.addAttribute("op", "删除");
return "user/edit";
} @RequestMapping(value = "/{id}/delete", method = RequestMethod.POST)
public String delete(@PathVariable("id") Long id, RedirectAttributes redirectAttributes) {
userService.deleteUser(id);
redirectAttributes.addFlashAttribute("msg", "删除成功");
return "redirect:/user";
} @RequestMapping(value = "/{id}/changePassword", method = RequestMethod.GET)
public String showChangePasswordForm(@PathVariable("id") Long id, Model model) {
model.addAttribute("user", userService.findOne(id));
model.addAttribute("op", "修改密码");
return "user/changePassword";
} @RequestMapping(value = "/{id}/changePassword", method = RequestMethod.POST)
public String changePassword(@PathVariable("id") Long id, String newPassword, RedirectAttributes redirectAttributes) {
userService.changePassword(id, newPassword);
redirectAttributes.addFlashAttribute("msg", "修改密码成功");
return "redirect:/user";
} }

UserController

 @Controller
@RequestMapping("/client")
public class ClientController { @Autowired
private ClientService clientService; @RequestMapping(method = RequestMethod.GET)
public String list(Model model) {
model.addAttribute("clientList", clientService.findAll());
return "client/list";
} @RequestMapping(value = "/create", method = RequestMethod.GET)
public String showCreateForm(Model model) {
model.addAttribute("client", new Client());
model.addAttribute("op", "新增");
return "client/edit";
} @RequestMapping(value = "/create", method = RequestMethod.POST)
public String create(Client client, RedirectAttributes redirectAttributes) {
clientService.createClient(client);
redirectAttributes.addFlashAttribute("msg", "新增成功");
return "redirect:/client";
} @RequestMapping(value = "/{id}/update", method = RequestMethod.GET)
public String showUpdateForm(@PathVariable("id") Long id, Model model) {
model.addAttribute("client", clientService.findOne(id));
model.addAttribute("op", "修改");
return "client/edit";
} @RequestMapping(value = "/{id}/update", method = RequestMethod.POST)
public String update(Client client, RedirectAttributes redirectAttributes) {
clientService.updateClient(client);
redirectAttributes.addFlashAttribute("msg", "修改成功");
return "redirect:/client";
} @RequestMapping(value = "/{id}/delete", method = RequestMethod.GET)
public String showDeleteForm(@PathVariable("id") Long id, Model model) {
model.addAttribute("client", clientService.findOne(id));
model.addAttribute("op", "删除");
return "client/edit";
} @RequestMapping(value = "/{id}/delete", method = RequestMethod.POST)
public String delete(@PathVariable("id") Long id, RedirectAttributes redirectAttributes) {
clientService.deleteClient(id);
redirectAttributes.addFlashAttribute("msg", "删除成功");
return "redirect:/client";
} }

ClientController

授权控制器:AuthorizeController

 @Controller
public class AuthorizeController { @Autowired
private OAuthService oAuthService;
@Autowired
private ClientService clientService; @RequestMapping("/authorize")
public Object authorize(
Model model,
HttpServletRequest request)
throws URISyntaxException, OAuthSystemException { try {
//构建OAuth 授权请求
OAuthAuthzRequest oauthRequest = new OAuthAuthzRequest(request); //检查传入的客户端id是否正确
if (!oAuthService.checkClientId(oauthRequest.getClientId())) {
OAuthResponse response =
OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST)
.setError(OAuthError.TokenResponse.INVALID_CLIENT)
.setErrorDescription(Constants.INVALID_CLIENT_DESCRIPTION)
.buildJSONMessage();
return new ResponseEntity(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
} Subject subject = SecurityUtils.getSubject();
//如果用户没有登录,跳转到登陆页面
if(!subject.isAuthenticated()) {
if(!login(subject, request)) {//登录失败时跳转到登陆页面
model.addAttribute("client", clientService.findByClientId(oauthRequest.getClientId()));
return "oauth2login";
}
} String username = (String)subject.getPrincipal();
//生成授权码
String authorizationCode = null;
//responseType目前仅支持CODE,另外还有TOKEN
String responseType = oauthRequest.getParam(OAuth.OAUTH_RESPONSE_TYPE);
if (responseType.equals(ResponseType.CODE.toString())) {
OAuthIssuerImpl oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());
authorizationCode = oauthIssuerImpl.authorizationCode();
oAuthService.addAuthCode(authorizationCode, username);
} //进行OAuth响应构建
OAuthASResponse.OAuthAuthorizationResponseBuilder builder =
OAuthASResponse.authorizationResponse(request, HttpServletResponse.SC_FOUND);
//设置授权码
builder.setCode(authorizationCode);
//得到到客户端重定向地址
String redirectURI = oauthRequest.getParam(OAuth.OAUTH_REDIRECT_URI); //构建响应
final OAuthResponse response = builder.location(redirectURI).buildQueryMessage(); //根据OAuthResponse返回ResponseEntity响应
HttpHeaders headers = new HttpHeaders();
headers.setLocation(new URI(response.getLocationUri()));
return new ResponseEntity(headers, HttpStatus.valueOf(response.getResponseStatus()));
} catch (OAuthProblemException e) { //出错处理
String redirectUri = e.getRedirectUri();
if (OAuthUtils.isEmpty(redirectUri)) {
//告诉客户端没有传入redirectUri直接报错
return new ResponseEntity("OAuth callback url needs to be provided by client!!!", HttpStatus.NOT_FOUND);
} //返回错误消息(如?error=)
final OAuthResponse response =
OAuthASResponse.errorResponse(HttpServletResponse.SC_FOUND)
.error(e).location(redirectUri).buildQueryMessage();
HttpHeaders headers = new HttpHeaders();
headers.setLocation(new URI(response.getLocationUri()));
return new ResponseEntity(headers, HttpStatus.valueOf(response.getResponseStatus()));
}
} private boolean login(Subject subject, HttpServletRequest request) {
if("get".equalsIgnoreCase(request.getMethod())) {
return false;
}
String username = request.getParameter("username");
String password = request.getParameter("password"); if(StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
return false;
} UsernamePasswordToken token = new UsernamePasswordToken(username, password); try {
subject.login(token);
return true;
} catch (Exception e) {
request.setAttribute("error", "登录失败:" + e.getClass().getName());
return false;
}
}
}

访问令牌控制器:AccessTokenController

 @RestController
public class AccessTokenController { @Autowired
private OAuthService oAuthService; @Autowired
private UserService userService; @RequestMapping("/accessToken")
public HttpEntity token(HttpServletRequest request)
throws URISyntaxException, OAuthSystemException { try {
//构建OAuth请求
OAuthTokenRequest oauthRequest = new OAuthTokenRequest(request); //检查提交的客户端id是否正确
if (!oAuthService.checkClientId(oauthRequest.getClientId())) {
OAuthResponse response =
OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST)
.setError(OAuthError.TokenResponse.INVALID_CLIENT)
.setErrorDescription(Constants.INVALID_CLIENT_DESCRIPTION)
.buildJSONMessage();
return new ResponseEntity(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
} // 检查客户端安全KEY是否正确
if (!oAuthService.checkClientSecret(oauthRequest.getClientSecret())) {
OAuthResponse response =
OAuthASResponse.errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
.setError(OAuthError.TokenResponse.UNAUTHORIZED_CLIENT)
.setErrorDescription(Constants.INVALID_CLIENT_DESCRIPTION)
.buildJSONMessage();
return new ResponseEntity(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
} String authCode = oauthRequest.getParam(OAuth.OAUTH_CODE);
// 检查验证类型,此处只检查AUTHORIZATION_CODE类型,其他的还有PASSWORD或REFRESH_TOKEN
if (oauthRequest.getParam(OAuth.OAUTH_GRANT_TYPE).equals(GrantType.AUTHORIZATION_CODE.toString())) {
if (!oAuthService.checkAuthCode(authCode)) {
OAuthResponse response = OAuthASResponse
.errorResponse(HttpServletResponse.SC_BAD_REQUEST)
.setError(OAuthError.TokenResponse.INVALID_GRANT)
.setErrorDescription("错误的授权码")
.buildJSONMessage();
return new ResponseEntity(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));
}
} //生成Access Token
OAuthIssuer oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());
final String accessToken = oauthIssuerImpl.accessToken();
oAuthService.addAccessToken(accessToken, oAuthService.getUsernameByAuthCode(authCode)); //生成OAuth响应
OAuthResponse response = OAuthASResponse
.tokenResponse(HttpServletResponse.SC_OK)
.setAccessToken(accessToken)
.setExpiresIn(String.valueOf(oAuthService.getExpireIn()))
.buildJSONMessage(); //根据OAuthResponse生成ResponseEntity
return new ResponseEntity(response.getBody(), HttpStatus.valueOf(response.getResponseStatus())); } catch (OAuthProblemException e) {
//构建错误响应
OAuthResponse res = OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST).error(e)
.buildJSONMessage();
return new ResponseEntity(res.getBody(), HttpStatus.valueOf(res.getResponseStatus()));
}
} }

资源控制器:UserInfoController

 @RestController
public class UserInfoController { @Autowired
private OAuthService oAuthService; @RequestMapping("/userInfo")
public HttpEntity userInfo(HttpServletRequest request) throws OAuthSystemException {
try { //构建OAuth资源请求
OAuthAccessResourceRequest oauthRequest = new OAuthAccessResourceRequest(request, ParameterStyle.QUERY);
//获取Access Token
String accessToken = oauthRequest.getAccessToken(); //验证Access Token
if (!oAuthService.checkAccessToken(accessToken)) {
// 如果不存在/过期了,返回未验证错误,需重新验证
OAuthResponse oauthResponse = OAuthRSResponse
.errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
.setRealm(Constants.RESOURCE_SERVER_NAME)
.setError(OAuthError.ResourceResponse.INVALID_TOKEN)
.buildHeaderMessage(); HttpHeaders headers = new HttpHeaders();
headers.add(OAuth.HeaderType.WWW_AUTHENTICATE, oauthResponse.getHeader(OAuth.HeaderType.WWW_AUTHENTICATE));
return new ResponseEntity(headers, HttpStatus.UNAUTHORIZED);
}
//返回用户名
String username = oAuthService.getUsernameByAccessToken(accessToken);
return new ResponseEntity(username, HttpStatus.OK);
} catch (OAuthProblemException e) {
//检查是否设置了错误码
String errorCode = e.getError();
if (OAuthUtils.isEmpty(errorCode)) {
OAuthResponse oauthResponse = OAuthRSResponse
.errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
.setRealm(Constants.RESOURCE_SERVER_NAME)
.buildHeaderMessage(); HttpHeaders headers = new HttpHeaders();
headers.add(OAuth.HeaderType.WWW_AUTHENTICATE, oauthResponse.getHeader(OAuth.HeaderType.WWW_AUTHENTICATE));
return new ResponseEntity(headers, HttpStatus.UNAUTHORIZED);
} OAuthResponse oauthResponse = OAuthRSResponse
.errorResponse(HttpServletResponse.SC_UNAUTHORIZED)
.setRealm(Constants.RESOURCE_SERVER_NAME)
.setError(e.getError())
.setErrorDescription(e.getDescription())
.setErrorUri(e.getUri())
.buildHeaderMessage(); HttpHeaders headers = new HttpHeaders();
headers.add(OAuth.HeaderType.WWW_AUTHENTICATE, oauthResponse.getHeader(OAuth.HeaderType.WWW_AUTHENTICATE));
return new ResponseEntity(HttpStatus.BAD_REQUEST);
}
}
}

(6)配置文件

 <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <context:property-placeholder location="classpath:resources.properties"/> <!-- 扫描注解Bean -->
<context:component-scan base-package="com.github.zhangkaitao.shiro.chapter17">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan> <!-- 开启AOP监听 只对当前配置文件有效 -->
<aop:aspectj-autoproxy expose-proxy="true"/> <!-- 数据源 -->
<!--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-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<!-- 基本属性 url、user、password -->
<property name="url" value="${connection.url}"/>
<property name="username" value="${connection.username}"/>
<property name="password" value="${connection.password}"/> <!-- 配置初始化大小、最小、最大 -->
<property name="initialSize" value="${druid.initialSize}"/>
<property name="minIdle" value="${druid.minIdle}"/>
<property name="maxActive" value="${druid.maxActive}"/> <!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="${druid.maxWait}"/>
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="${druid.timeBetweenEvictionRunsMillis}" /> <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="${druid.minEvictableIdleTimeMillis}" /> <property name="validationQuery" value="${druid.validationQuery}" />
<property name="testWhileIdle" value="${druid.testWhileIdle}" />
<property name="testOnBorrow" value="${druid.testOnBorrow}" />
<property name="testOnReturn" value="${druid.testOnReturn}" /> <!-- 打开PSCache,并且指定每个连接上PSCache的大小 如果用Oracle,则把poolPreparedStatements配置为true,mysql可以配置为false。-->
<property name="poolPreparedStatements" value="${druid.poolPreparedStatements}" />
<property name="maxPoolPreparedStatementPerConnectionSize" value="${druid.maxPoolPreparedStatementPerConnectionSize}" /> <!-- 配置监控统计拦截的filters -->
<property name="filters" value="${druid.filters}" /> </bean> <bean id="dataSourceProxy" class="org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy">
<property name="targetDataSource" ref="dataSource"/>
</bean> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<constructor-arg ref="dataSourceProxy"/>
</bean> <!--事务管理器配置-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSourceProxy"/>
</bean> <tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice> <aop:config expose-proxy="true" proxy-target-class="true">
<!-- 只对业务逻辑层实施事务 -->
<aop:pointcut id="txPointcut" expression="execution(* com.github.zhangkaitao.shiro.chapter16..service..*+.*(..))"/>
<aop:advisor id="txAdvisor" advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config> <import resource="classpath:spring-config-cache.xml"/>
<import resource="classpath:spring-config-shiro.xml"/>
</beans>

spring-config.xml

 <beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd"> <context:property-placeholder location="classpath:resources.properties"/> <!-- 开启controller注解支持 -->
<!-- 注意事项请参考:http://jinnianshilongnian.iteye.com/blog/1762632 -->
<context:component-scan base-package="com.github.zhangkaitao.**.web.controller" use-default-filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
<context:include-filter type="annotation"
expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan> <mvc:annotation-driven>
</mvc:annotation-driven> <!-- 当在web.xml 中 DispatcherServlet使用 <url-pattern>/</url-pattern> 映射时,能映射静态资源 -->
<mvc:default-servlet-handler/> <!-- 静态资源映射 -->
<mvc:resources mapping="/static/**" location="/WEB-INF/static/"/> <!-- 默认的视图解析器 在上边的解析错误时使用 (默认使用html)- -->
<bean id="defaultViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:order="1">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="contentType" value="text/html"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean> <!-- 控制器异常处理 -->
<bean id="exceptionHandlerExceptionResolver" class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver">
</bean> <bean class="com.github.zhangkaitao.shiro.chapter17.web.exception.DefaultExceptionHandler"/> <import resource="spring-mvc-shiro.xml"/> </beans>

spring-mvc.xml

 <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <aop:config proxy-target-class="true"></aop:config>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean> </beans>

spring-mvc-shiro.xml

 <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 缓存管理器 -->
<bean id="cacheManager" class="com.github.zhangkaitao.shiro.spring.SpringCacheManagerWrapper">
<property name="cacheManager" ref="springCacheManager"/>
</bean> <!-- 凭证匹配器 -->
<bean id="credentialsMatcher" class="com.github.zhangkaitao.shiro.chapter17.credentials.RetryLimitHashedCredentialsMatcher">
<constructor-arg ref="cacheManager"/>
<property name="hashAlgorithmName" value="md5"/>
<property name="hashIterations" value="2"/>
<property name="storedCredentialsHexEncoded" value="true"/>
</bean> <!-- Realm实现 -->
<bean id="userRealm" class="com.github.zhangkaitao.shiro.chapter17.realm.UserRealm">
<property name="credentialsMatcher" ref="credentialsMatcher"/>
<property name="cachingEnabled" value="false"/>
<!--<property name="authenticationCachingEnabled" value="true"/>-->
<!--<property name="authenticationCacheName" value="authenticationCache"/>-->
<!--<property name="authorizationCachingEnabled" value="true"/>-->
<!--<property name="authorizationCacheName" value="authorizationCache"/>-->
</bean> <!-- 会话ID生成器 -->
<bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/> <!-- 会话Cookie模板 -->
<bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg value="sid"/>
<property name="httpOnly" value="true"/>
<property name="maxAge" value="-1"/>
</bean> <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
<constructor-arg value="rememberMe"/>
<property name="httpOnly" value="true"/>
<property name="maxAge" value="2592000"/><!-- 30天 -->
</bean> <!-- rememberMe管理器 -->
<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
<!-- rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)-->
<property name="cipherKey"
value="#{T(org.apache.shiro.codec.Base64).decode('4AvVhmFLUs0KTA3Kprsdag==')}"/>
<property name="cookie" ref="rememberMeCookie"/>
</bean> <!-- 会话DAO -->
<bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO">
<property name="activeSessionsCacheName" value="shiro-activeSessionCache"/>
<property name="sessionIdGenerator" ref="sessionIdGenerator"/>
</bean> <!-- 会话验证调度器 -->
<bean id="sessionValidationScheduler" class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler">
<property name="sessionValidationInterval" value="1800000"/>
<property name="sessionManager" ref="sessionManager"/>
</bean> <!-- 会话管理器 -->
<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
<property name="globalSessionTimeout" value="1800000"/>
<property name="deleteInvalidSessions" value="true"/>
<property name="sessionValidationSchedulerEnabled" value="true"/>
<property name="sessionValidationScheduler" ref="sessionValidationScheduler"/>
<property name="sessionDAO" ref="sessionDAO"/>
<property name="sessionIdCookieEnabled" value="true"/>
<property name="sessionIdCookie" ref="sessionIdCookie"/>
</bean> <!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="userRealm"/>
<property name="sessionManager" ref="sessionManager"/>
<property name="cacheManager" ref="cacheManager"/>
<property name="rememberMeManager" ref="rememberMeManager"/>
</bean> <!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) -->
<bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
<property name="arguments" ref="securityManager"/>
</bean> <!-- 基于Form表单的身份验证过滤器 -->
<bean id="formAuthenticationFilter" class="org.apache.shiro.web.filter.authc.FormAuthenticationFilter">
<property name="usernameParam" value="username"/>
<property name="passwordParam" value="password"/>
<property name="rememberMeParam" value="rememberMe"/>
<property name="loginUrl" value="/login"/>
</bean> <!-- Shiro的Web过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="/login"/>
<property name="filters">
<util:map>
<entry key="authc" value-ref="formAuthenticationFilter"/>
</util:map>
</property>
<property name="filterChainDefinitions">
<value>
/ = anon
/login = authc
/logout = logout /authorize=anon
/accessToken=anon
/userInfo=anon /** = user
</value>
</property>
</bean> <!-- Shiro生命周期处理器-->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> </beans>

spring-config-shiro.xml

 <?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="springCacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager" ref="ehcacheManager"/>
</bean> <!--ehcache-->
<bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:ehcache/ehcache.xml"/>
</bean> </beans>

spring-config-cache.xml

 <?xml version="1.0" encoding="UTF-8"?>
<ehcache name="es"> <diskStore path="java.io.tmpdir"/> <!-- 登录记录缓存 锁定10分钟 -->
<cache name="passwordRetryCache"
maxEntriesLocalHeap="2000"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="true">
</cache> <cache name="authorizationCache"
maxEntriesLocalHeap="2000"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="true">
</cache> <cache name="authenticationCache"
maxEntriesLocalHeap="2000"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="true">
</cache> <cache name="shiro-activeSessionCache"
maxEntriesLocalHeap="2000"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="true">
</cache> <cache name="code-cache"
maxEntriesLocalHeap="2000"
eternal="false"
timeToIdleSeconds="3600"
timeToLiveSeconds="0"
overflowToDisk="false"
statistics="true">
</cache> </ehcache>

ehcache.xml

 #dataSource configure
connection.url=jdbc:mysql://localhost:3306/shiro-oauth2
connection.username=root
connection.password= #druid datasource
#参考 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
druid.initialSize=10
druid.minIdle=10
druid.maxActive=50
druid.maxWait=60000
druid.timeBetweenEvictionRunsMillis=60000
druid.minEvictableIdleTimeMillis=300000
druid.validationQuery=SELECT 'x'
druid.testWhileIdle=true
druid.testOnBorrow=false
druid.testOnReturn=false
druid.poolPreparedStatements=true
druid.maxPoolPreparedStatementPerConnectionSize=20
druid.filters=wall,stat #shiro
password.algorithmName=md5
password.hashIterations=2

resource.properties

(7)服务器的维护

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

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

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

3.客户端

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

具体请看《2017.2.16 开涛shiro教程-第十七章-OAuth2集成(一)客户端》

2017.2.16 开涛shiro教程-第十七章-OAuth2集成(一)服务器端的更多相关文章

  1. 2017.2.16 开涛shiro教程-第十七章-OAuth2集成(二)客户端

    原博客地址:http://jinnianshilongnian.iteye.com/blog/2018398 根据下载的pdf学习. 开涛shiro教程-第十七章-OAuth2集成 3.客户端 客户端 ...

  2. 2017.2.15 开涛shiro教程-第二十一章-授予身份与切换身份(二) controller

    原博客地址:http://jinnianshilongnian.iteye.com/blog/2018398 根据下载的pdf学习. 开涛shiro教程-第二十一章-授予身份与切换身份(二) 1.回顾 ...

  3. 2017.2.15 开涛shiro教程-第二十一章-授予身份与切换身份(一) table、entity、service、dao

    原博客地址:http://jinnianshilongnian.iteye.com/blog/2018398 根据下载的pdf学习. 第二十一章 授予身份与切换身份(一) 1.使用场景 某个领导因为某 ...

  4. 2017.2.12 开涛shiro教程-第七章-与Web集成

    2017.2.9 开涛shiro教程-第七章-与Web集成(一) 原博客地址:http://jinnianshilongnian.iteye.com/blog/2018398 根据下载的pdf学习. ...

  5. 2017.2.7 开涛shiro教程-第六章-Realm及相关对象(四)

    原博客地址:http://jinnianshilongnian.iteye.com/blog/2018398 根据下载的pdf学习. 第六章 Realm及相关对象(四) 1.Subject的代码结构 ...

  6. 2017.2.7 开涛shiro教程-第六章-Realm及相关对象(二)

    原博客地址:http://jinnianshilongnian.iteye.com/blog/2018398 根据下载的pdf学习. 第六章 Realm及相关对象(二) 1.Authenticatio ...

  7. 2017.2.7 开涛shiro教程-第六章-Realm及相关对象(一)

    原博客地址:http://jinnianshilongnian.iteye.com/blog/2018398 根据下载的pdf学习. 第六章 Realm及相关对象 1.用户.角色.权限的关系 用户和角 ...

  8. 2017.2.7 开涛shiro教程-第六章-Realm及相关对象(三)

    原博客地址:http://jinnianshilongnian.iteye.com/blog/2018398 根据下载的pdf学习. 第六章 Realm及相关对象(三) 1.准备3个Realm MyR ...

  9. 2017.4.12 开涛shiro教程-第十八章-并发登录人数控制

    原博客地址:http://jinnianshilongnian.iteye.com/blog/2018398 根据下载的pdf学习. 开涛shiro教程-第十八章-并发登录人数控制 shiro中没有提 ...

随机推荐

  1. [译]如何检查python中的值是否为nan?

    float('nan')是Nan不是一个数字,我该如何判断一个值为nan,有什么简单的方法么? 使用math.isnan()来进行判断 >>> import math >> ...

  2. algorithm 头文件

    非修改性序列操作(12个) 循环 对序列中的每个元素执行某操作 for_each() 查找 在序列中找出某个值的第一次出现的位置 find() 在序列中找出符合某谓词的第一个元素 find_if() ...

  3. Socket通信——服务器和客户端相互通信

    所谓socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄.应用程序通常通过"套接字"向网络发出请求或者应答网络请求.  Socket和S ...

  4. The following signatures couldn't be verified because the public key is not available 解决方法

    今天试图把 deepin 的软件源加到我到 Ubuntu 16.04 中去. 在 deepin wiki 上看到一个教程. 在 /etc/apt/sources.list 中加上 deepin 的软件 ...

  5. codeforce div2 426 D. The Bakery

    D. The Bakery time limit per test 2.5 seconds memory limit per test 256 megabytes input standard inp ...

  6. query带进度上传插件Uploadify(ASP.NET版本)使用

    原文发布时间为:2010-05-13 -- 来源于本人的百度文章 [由搬家工具导入] 本文将带给大家很帅的jquery上传插件,ASP.NET版本的哦,这个插件是Uploadify实现的效果非常不错, ...

  7. JS允许找不到对象 不必判断是否存在某个div

    原文发布时间为:2009-11-10 -- 来源于本人的百度文章 [由搬家工具导入] 加一个try catch 即可。。。。 例子: for(var i=0;i<10;i++)     {    ...

  8. Linux下的软连接和硬链接

    由于教学上的原因,需要下载Android源码,后来使用repo时,系统提示需要python2,我的系统中是两个都有的,但是默认是python3,无法下载,通过创建了个链接搞定,下面就来说说linux下 ...

  9. Bioconda安装与使用

    1.  Bioconda是一个自动化管理生物信息软件的工具,就像APPstore.360软件管家一样. Bioconda的优点是安装简单,各个软件依赖的环境一同打包且相互隔离,非常适合在服务器中建立自 ...

  10. 误加all_load引起的程序报错

    一.为什么要加-all_load 在64位的mac系统和iOS系统下,链接器有一个bug,会导致只包含有类别的静态库无法使用-ObjC标志来加载文件.解决方法是使用-all_load或者-force_ ...