1、Shiro是Apache下的一個開源項目,我們稱之為Apache Shiro。它是一個很易用與Java項目的的安全框架,提供了認證、授權、加密、會話管理,與spring Security 一樣都是做一個權限的安全框架,但是與Spring Security 相比,在於 Shiro 使用了比較簡單易懂易於使用的授權方式。shiro屬於輕量級框架,相對於security簡單的多,也沒有security那么復雜。所以我這里也是簡單介紹一下shiro的使用。
2、非常簡單;其基本功能點如下圖所示:
Authentication:身份認證/登錄,驗證用戶是不是擁有相應的身份;
Authorization:授權,即權限驗證,驗證某個已認證的用戶是否擁有某個權限;即判斷用戶是否能做事情,常見的如:驗證某個用戶是否擁有某個角色。或者細粒度的驗證某個用戶對某個資源是否具有某個權限;
Session Manager:會話管理,即用戶登錄后就是一次會話,在沒有退出之前,它的所有信息都在會話中;會話可以是普通JavaSE環境的,也可以是如Web環境的;
Cryptography:加密,保護數據的安全性,如密碼加密存儲到數據庫,而不是明文存儲;
Web Support:Web支持,可以非常容易的集成到Web環境;
Caching:緩存,比如用戶登錄后,其用戶信息、擁有的角色/權限不必每次去查,這樣可以提高效率;
Concurrency:shiro支持多線程應用的並發驗證,即如在一個線程中開啟另一個線程,能把權限自動傳播過去;
Testing:提供測試支持;
Run As:允許一個用戶假裝為另一個用戶(如果他們允許)的身份進行訪問;
Remember Me:記住我,這個是非常常見的功能,即一次登錄后,下次再來的話不用登錄了。
記住一點,Shiro不會去維護用戶、維護權限;這些需要我們自己去設計/提供;然后通過相應的接口注入給Shiro即可。
3、這里我就簡單介紹一下springboot和shiro整合與基本使用。
1)目錄結構

2)需要的基礎包:pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.troy</groupId>
<artifactId>springshiro</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.6.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>1.5.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>1.5.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>1.5.6.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-Java</artifactId>
<version>5.1.9</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.4</version>
</dependency>
</dependencies>
</project>
3)基本配置application.yml
server:
port: 8082
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/spring_shiro?useUnicode=true&characterEncoding=UTF-8
username: root
password: root
type: com.alibaba.druid.pool.DruidDataSource
jpa:
show-sql: true
hibernate:
ddl-auto: update
http:
encoding:
charset: utf-8
enabled: true
4)這里我們基本需要3個實體,用戶,角色和權限
(1)角色:User.class
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(unique = true)
private String name;
private Integer password;
@OneToMany(cascade = CascadeType.ALL,mappedBy = "user")
private List<Role> roles;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Role> getRoles() {
return roles;
}
public void setRoles(List<Role> roles) {
this.roles = roles;
}
public Integer getPassword() {
return password;
}
public void setPassword(Integer password) {
this.password = password;
}
}
注:這里我只考慮一個用戶對多個角色,不考慮多對多的關系
(2)角色:Role.class
@Entity
public class Role {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String roleName;
@ManyToOne(fetch = FetchType.EAGER)
private User user;
@OneToMany(cascade = CascadeType.ALL,mappedBy = "role")
private List<Permission> permissions;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String roleName) {
this.roleName = roleName;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public List<Permission> getPermissions() {
return permissions;
}
public void setPermissions(List<Permission> permissions) {
this.permissions = permissions;
}
}
(3)權限:Permission.class
@Entity
public class Permission {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String permission;
@ManyToOne(fetch = FetchType.EAGER)
private Role role;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getPermission() {
return permission;
}
public void setPermission(String permission) {
this.permission = permission;
}
public Role getRole() {
return role;
}
public void setRole(Role role) {
this.role = role;
}
}
5)然后就是配置對應的驗證,以及過濾條件
(1)驗證,以及權限的添加MyShiroRealm.class
//實現AuthorizingRealm接口用戶用戶認證
public class MyShiroRealm extends AuthorizingRealm{
//用於用戶查詢
@Autowired
private ILoginService loginService;
//角色權限和對應權限添加
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//獲取登錄用戶名
String name= (String) principalCollection.getPrimaryPrincipal();
//查詢用戶名稱
User user = loginService.findByName(name);
//添加角色和權限
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
for (Role role:user.getRoles()) {
//添加角色
simpleAuthorizationInfo.addRole(role.getRoleName());
for (Permission permission:role.getPermissions()) {
//添加權限
simpleAuthorizationInfo.addStringPermission(permission.getPermission());
}
}
return simpleAuthorizationInfo;
}
//用戶認證
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//加這一步的目的是在Post請求的時候會先進認證,然后在到請求
if (authenticationToken.getPrincipal() == null) {
return null;
}
//獲取用戶信息
String name = authenticationToken.getPrincipal().toString();
User user = loginService.findByName(name);
if (user == null) {
//這里返回后會報出對應異常
return null;
} else {
//這里驗證authenticationToken和simpleAuthenticationInfo的信息
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(name, user.getPassword().toString(), getName());
return simpleAuthenticationInfo;
}
}
}
(2)過濾配置:ShiroConfiguration.class
@Configuration
public class ShiroConfiguration {
//將自己的驗證方式加入容器
@Bean
public MyShiroRealm myShiroRealm() {
MyShiroRealm myShiroRealm = new MyShiroRealm();
return myShiroRealm;
}
//權限管理,配置主要是Realm的管理認證
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
return securityManager;
}
//Filter工廠,設置對應的過濾條件和跳轉條件
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String,String> map = new HashMap<String, String>();
//登出
map.put("/logout","logout");
//對所有用戶認證
map.put("/**","authc");
//登錄
shiroFilterFactoryBean.setLoginUrl("/login");
//首頁
shiroFilterFactoryBean.setSuccessUrl("/index");
//錯誤頁面,認證不通過跳轉
shiroFilterFactoryBean.setUnauthorizedUrl("/error");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
//加入注解的使用,不加入這個注解不生效
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
6)接下來就是數據訪問層、業務層、以及控制層
(1)數據層:BaseRepository.class,UserRepository.class,RoleRepository.class
@NoRepositoryBean
public interface BaseRepository<T,I extends Serializable> extends PagingAndSortingRepository<T,I>,JpaSpecificationExecutor<T>{
}
public interface UserRepository extends BaseRepository<User,Long>{
User findByName(String name);
}
public interface RoleRepository extends BaseRepository<Role,Long> {
}
(2)業務層:LoginServiceImpl.class
@Service
@Transactional
public class LoginServiceImpl implements ILoginService {
@Autowired
private UserRepository userRepository;
@Autowired
private RoleRepository roleRepository;
//添加用戶
@Override
public User addUser(Map<String, Object> map) {
User user = new User();
user.setName(map.get("username").toString());
user.setPassword(Integer.valueOf(map.get("password").toString()));
userRepository.save(user);
return user;
}
//添加角色
@Override
public Role addRole(Map<String, Object> map) {
User user = userRepository.findOne(Long.valueOf(map.get("userId").toString()));
Role role = new Role();
role.setRoleName(map.get("roleName").toString());
role.setUser(user);
Permission permission1 = new Permission();
permission1.setPermission("create");
permission1.setRole(role);
Permission permission2 = new Permission();
permission2.setPermission("update");
permission2.setRole(role);
List<Permission> permissions = new ArrayList<Permission>();
permissions.add(permission1);
permissions.add(permission2);
role.setPermissions(permissions);
roleRepository.save(role);
return role;
}
//查詢用戶通過用戶名
@Override
public User findByName(String name) {
return userRepository.findByName(name);
}
}
(3)控制層:LoginResource.class
@RestController
public class LoginResource {
@Autowired
private ILoginService loginService;
//退出的時候是get請求,主要是用於退出
@RequestMapping(value = "/login",method = RequestMethod.GET)
public String login(){
return "login";
}
//post登錄
@RequestMapping(value = "/login",method = RequestMethod.POST)
public String login(@RequestBody Map map){
//添加用戶認證信息
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(
map.get("username").toString(),
map.get("password").toString());
//進行驗證,這里可以捕獲異常,然后返回對應信息
subject.login(usernamePasswordToken);
return "login";
}
@RequestMapping(value = "/index")
public String index(){
return "index";
}
//登出
@RequestMapping(value = "/logout")
public String logout(){
return "logout";
}
//錯誤頁面展示
@RequestMapping(value = "/error",method = RequestMethod.POST)
public String error(){
return "error ok!";
}
//數據初始化
@RequestMapping(value = "/addUser")
public String addUser(@RequestBody Map<String,Object> map){
User user = loginService.addUser(map);
return "addUser is ok! \n" + user;
}
//角色初始化
@RequestMapping(value = "/addRole")
public String addRole(@RequestBody Map<String,Object> map){
Role role = loginService.addRole(map);
return "addRole is ok! \n" + role;
}
//注解的使用
@RequiresRoles("admin")
@RequiresPermissions("create")
@RequestMapping(value = "/create")
public String create(){
return "Create success!";
}
}
注:這里對於注解的使用,在最后一個很重要!
7)shiro的使用基本上就是這樣子了,主要是權限的控制,其他的主要是做跳轉和切換使用
8)最后配上數據庫信息:結合控制層觀看
user:

role:

permission:


