Live-Server-4-Shiro的簡單使用
Apache Shiro是Java的一個安全框架,主要完成:認證、授權、加密、會話管理、快取等功能,而且API簡單,越來越多人使用該框架。
Shiro介紹
基本功能如下:
- Authentications:身份認證/登入,驗證使用者是否有對應的身份。
- Authorization:授權,判斷使用者是否擁有某個許可權,比如當前使用者是否擁有後臺管理員管理的許可權等。
- SessionManager:會話管理,使用者登入後的一次會話,在還沒退出之前,都是通過會話來識別使用者。
- Cryptography:加密,保證資料的安全性。
- Web Support:便於整合到Web環境中。
- Cacheing:快取,儲存使用者登入、許可權等資訊。
Shiro使用
通過上述的簡介,可以得知,如果我們要對使用者分不同的許可權,如管理員、普通使用者;要對會話進行管理,就需要用到Shiro這個安全框架。Shiro在SpringBoot中的使用就需要建立一個配置類,同時要配置Realm。
一、Shiro配置類
Shiro配置類中,需要配置生命週期處理器、SecurityManager,在該安全管理器中配置Realm、ShiroFilter過濾器、aop註解支援等。
/**
* spring-boot整合shiro配置類
*/
@Configuration
public class ShiroConfiguration {
/**
* 例項化lifecycleBeanPostProcessor
* shiro生命週期處理器
*
* @return
*/
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**
* 例項化DefaultAdvisorAutoProxyCreator
*
* @return
*/
@Bean
public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator daap = new DefaultAdvisorAutoProxyCreator();
daap.setProxyTargetClass(true);
return daap;
}
/**
* 例項化自定義realm
*
* @return
*/
@Bean
public Realm getRealm() {
return new UserRealm();
}
/**
* 例項化securityManager
*
* @return
*/
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(getRealm());
return securityManager;
}
/**
* 例項化shiroFilter
*
* @return
*/
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean getShiroFilterFactoryBean() {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 必須設定 SecurityManager
shiroFilterFactoryBean.setSecurityManager(getDefaultWebSecurityManager());
// 如果不設定預設會自動尋找Web工程根目錄下的"/login.jsp"頁面;
shiroFilterFactoryBean.setUnauthorizedUrl("/admin/403");
return shiroFilterFactoryBean;
}
/**
* 開啟shiro aop註解支援.
* 使用代理方式;所以需要開啟程式碼支援;
*
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
org.apache.shiro.mgt.SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
複製程式碼
二、Realm
一個應用中可以有一個或者多個Realm,可以通過JDBC、記憶體來實現。在該系列文章中Server-2 MyBatis逆向生成中,觀察仔細的朋友會發現,我建立了permission、role、role-permission三張資料表,分別表示了許可權型別、角色型別、角色所擁有的許可權這三種含義。這三張表與Realm之間有什麼關係呢?在專案中建立了UserRealm使用者認證、授權介面。具體程式碼如下:
public class UserRealm extends AuthorizingRealm {
private final String realmName = "UserRealm.class";
@Autowired
private UserMapper userMapper;
@Autowired
private PermissionService permissionService;
@Autowired
private RoleMapper roleMapper;
//授權方法
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
Object principal = principals.getPrimaryPrincipal();
try {
if (principal instanceof String) {
UserExample example = new UserExample();
UserExample.Criteria criteria = example.createCriteria();
criteria.andAccountEqualTo((String) principal);
Integer roleId = userMapper.selectByExample(example).get(0).getRoleId();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
Role role = roleMapper.selectByPrimaryKey(roleId);
info.addRole(role.getName());
Set<PermissionVO> permissions = permissionService.getPermissionsBy(roleId);
for (PermissionVO permission : permissions) {
info.addStringPermission(permission.getName());
}
return info;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 認證
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
Object principal = token.getPrincipal();
if (principal instanceof String) {
String phone = (String) principal;
UserExample example = new UserExample();
UserExample.Criteria criteria = example.createCriteria();
criteria.andAccountEqualTo(phone);
List<User> users = userMapper.selectByExample(example);
if (null != users && users.size() > 0) {
User user = users.get(0);
return new SimpleAccount(user.getAccount(),user.getPassword(),realmName);
}
}
return null;
}
}
複製程式碼
在UserRealm中,可以看出,其實認證和授權都是通過資料庫的資料來進行判斷,比如認證,其實就是通過MyBatis來查詢資料庫中是否存在該使用者;授權方法,則是傳入使用者的賬號,檢視該使用者的角色,再查詢該角色所擁有的許可權,將賬號、角色、許可權資訊儲存到AuthorizationInfo中,即完成使用者授權。
三、使用者認證
下面幾行簡單的程式碼就完成了使用者的認證。
Subject subject = SecurityUtils.getSubject(); //獲取Subject
AuthenticationToken token = new UsernamePasswordToken(account,MD5Util.encrypt(password)); //根據使用者賬號、密碼獲取令牌。
try{
subject.login(token);
}catch( ..){
...
}
複製程式碼
首先,第一行程式碼,getSubject(),這裡有個巨坑,我懷疑getSubject()這個方式是根據請求中是否帶有cookie,來判斷是新的subject還是已經存在的subject,因為如果沒有帶cookie的請求,每次獲取的subject都不一致。如果Subject不唯一,那麼會導致Session會話也不唯一,那麼session儲存的鍵值對(通常是一些狀態、引數)就無法使用,導致功能的異常。 如果登入(使用者認證)失敗,那麼就會丟擲不同的異常,比如UnknownAccountException(賬號不存在)、IncorrectCredentialsException(賬號密碼不匹配)、LockedAccountException(不允許登入)等。