一、Shiro是Apache下的一個開源項目,咱們稱之爲Apache Shiro。它是一個很易用與Java項目的的安全框架,提供了認證、受權、加密、會話管理,與spring Security 同樣都是作一個權限的安全框架,可是與Spring Security 相比,在於 Shiro 使用了比較簡單易懂易於使用的受權方式。shiro屬於輕量級框架,相對於security簡單的多,也沒有security那麼複雜。因此我這裏也是簡單介紹一下shiro的使用。mysql
二、很是簡單;其基本功能點以下圖所示:web
Authentication:身份認證/登陸,驗證用戶是否是擁有相應的身份;spring
Authorization:受權,即權限驗證,驗證某個已認證的用戶是否擁有某個權限;即判斷用戶是否能作事情,常見的如:驗證某個用戶是否擁有某個角色。或者細粒度的驗證某個用戶對某個資源是否具備某個權限;sql
Session Manager:會話管理,即用戶登陸後就是一次會話,在沒有退出以前,它的全部信息都在會話中;會話能夠是普通JavaSE環境的,也能夠是如Web環境的;數據庫
Cryptography:加密,保護數據的安全性,如密碼加密存儲到數據庫,而不是明文存儲;apache
Web Support:Web支持,能夠很是容易的集成到Web環境;緩存
Caching:緩存,好比用戶登陸後,其用戶信息、擁有的角色/權限沒必要每次去查,這樣能夠提升效率;安全
Concurrency:shiro支持多線程應用的併發驗證,即如在一個線程中開啓另外一個線程,能把權限自動傳播過去;springboot
Testing:提供測試支持;多線程
Run As:容許一個用戶僞裝爲另外一個用戶(若是他們容許)的身份進行訪問;
Remember Me:記住我,這個是很是常見的功能,即一次登陸後,下次再來的話不用登陸了。
記住一點,Shiro不會去維護用戶、維護權限;這些須要咱們本身去設計/提供;而後經過相應的接口注入給Shiro便可。
三、這裏我就簡單介紹一下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: