shiro介紹
shiro是一個權限管理架構,基于使用者-角色-權限。一個使用者可以有多個角色,一個角色有多個權限,每個權限指定了資源的通路。
shiro的原理是在所有請求之前設定一個filter,這個filter判斷哪些資源需要權限,哪些不要,對于不需要權限的直接放行,對于需要權限的,使用securityManager和realm進行身份驗證和授權,如果驗證失敗或者權限不足,都跳轉到登入頁面或者傳回錯誤資訊。這和我們自己使用賬号密碼判斷是一樣的,隻是這個架構讓我們少寫很多代碼。
spring-boot 內建shiro其實相當簡單。
加入依賴
//shiro
compile group: 'org.apache.shiro', name: 'shiro-spring', version: '1.4.0'
ShiroConfig
建立一個配置檔案,用來配置shiro
package com.example.shiro;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.realm.text.TextConfigurationRealm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Shiro 配置
*/
@Configuration
public class ShiroConfig {
//建立shiroFilterFactoryBean, 設定securityManager、配置filterChain
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean createShiroFilterFactoryBean() {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(createSecurityManager());
bean.setLoginUrl("/admin/web/login"); //登入頁面
Map<String, String> filterChainMap = new LinkedHashMap<>(); //這裡一定要是有序的
//這裡可以從資料庫中讀取權限-資源規則
filterChainMap.put("/api/v1/admin/login", "anon"); //登入不需要驗證權限
filterChainMap.put("/api/v1/admin/**", "authc"); //其他api需要權限
filterChainMap.put("/admin/web/login", "anon");//登入頁面不需要權限
filterChainMap.put("/admin/web/**", "authc");//其他頁面需要權限
bean.setFilterChainDefinitionMap(filterChainMap);
return bean;
}
//建立一個securityManager
@Bean(name = "securityManager")
public SecurityManager createSecurityManager() {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager(new UserRealm());
return manager;
}
}
建立realm
realm 主要用來驗證密碼和授權
@Component
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Override
public String getName() {
return "user-realm";
}
@Override
public boolean supports(AuthenticationToken token) {
return true;
}
//擷取使用者的驗證資訊,用于和token進行對比
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String userName = (String) token.getPrincipal();
User user = userService.findUserByAccount(userName);
if (user == null) {
return null;
}
return new SimpleAuthenticationInfo(user, user.getPassword(), getName());
}
//給使用者設定角色資訊
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
User user = (User) principals.getPrimaryPrincipal();
Set<String> roles=new HashSet<>();
roles.add(user.getRole());
return new SimpleAuthorizationInfo(roles);
}
//設定權限資訊
@Override
protected Collection<Permission> getPermissions(AuthorizationInfo info) {
return super.getPermissions(info);
}
}
登入
@PostMapping("/login")
@ResponseBody
public DtoWrap login(@RequestBody UserDto userDto) {
DtoWrap result = new DtoWrap();
Subject subject = SecurityUtils.getSubject();
AuthenticationToken token = new UsernamePasswordToken(userDto.getUserName(), userDto.getPassword());
try {
subject.login(token);
//if no exception, that's it, we're done!
} catch (AuthenticationException ae) {
throw new BusinessException("使用者名或密碼錯誤");
}
return result;
}