摘要:雖然,直接用和進行全家桶式的合作是最好不過的,但現實總是欺負我們這些沒辦法決定架構類型的娃子。并非按輸入順序。遍歷時只能全部輸出,而沒有順序。設想以下,若全局劫持在最前面,那么只要在襠下的,都早早被劫持了。底層是數組加單項鏈表加雙向鏈表。
1. 添加Shiro依賴雖然,直接用Spring Security和SpringBoot 進行“全家桶式”的合作是最好不過的,但現實總是欺負我們這些沒辦法決定架構類型的娃子。
Apache Shiro 也有其特殊之處滴。若需了解,可以轉戰到[Apache Shiro 簡介]
shiro的版本,看個人喜好哈,本文的版本為:
<shiro.version>1.3.2shiro.version>
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-coreartifactId>
<version>${shiro.version}version>
dependency>
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-webartifactId>
<version>${shiro.version}version>
dependency>
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-aspectjartifactId>
<version>${shiro.version}version>
dependency>
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-ehcacheartifactId>
<version>${shiro.version}version>
dependency>
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-quartzartifactId>
<version>${shiro.version}version>
dependency>
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-springartifactId>
<version>${shiro.version}version>
dependency>
2. shiroRealm
授權認證具體實現之地。通過繼承 AuthorizingRealm 進而實現,對登錄時的賬號密碼校驗功能
@Slf4j
public class ShiroRealm extends AuthorizingRealm {
@Autowired
private ShiroPermissionRepository shiroPermissionRepository;
/**
* 授權
*
* @param principalCollection 主要信息
* @return 授權信息
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
if (log.isInfoEnabled()){
log.info("Authorization begin");
}
String name= (String) principalCollection.getPrimaryPrincipal();
List role = shiroPermissionRepository.queryRoleByName(name);
if (role.isEmpty()){
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addRoles(role);
return simpleAuthorizationInfo;
}
return null;
}
/**
* 認證
*
* @param authenticationToken 認證token
* @return 認證結果
* @throws AuthenticationException 認證異常
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
if (log.isInfoEnabled()){
log.info("Authentication begin");
}
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
Object principal =token.getPrincipal();
Object credentials = token.getCredentials();
//校驗用戶名
checkBlank(principal,"用戶名不能為空");
//校驗密碼
checkBlank(credentials,"密碼不能為空");
//校驗姓名
String username = (String) principal;
UserPO userPO = shiroPermissionRepository.findAllByName(username);
if (userPO == null){
throw new AccountException("用戶名錯誤");
}
//校驗密碼
String password = (String) credentials;
if (!StringUtils.equals(password,userPO.getPassword())){
throw new AccountException("密碼錯誤");
}
return new SimpleAuthenticationInfo(principal, password, getName());
}
private void checkBlank(Object obj,String message){
if (obj instanceof String){
if (StringUtils.isBlank((String) obj)){
throw new AccountException(message);
}
}else if (obj == null){
throw new AccountException(message);
}
}
}
3. 配置ShiroConfig
將ShiroConfig、SecurityManager、ShiroFilterFactoryBean交給Spring管理.
ShiroRealm: 則上述所描述的ShiroRealm
SecurityManager: 管理 所有用戶 的安全操作
ShiroFilterFactoryBean: 配置Shiro的過濾器
@Configuration
public class ShiroConfig {
private final static String AUTHC_STR = "authc";
private final static String ANON_STR = "anon";
/**
* 驗證授權、認證
*
* @return shiroRealm 授權認證
*/
@Bean
public ShiroRealm shiroRealm(){
return new ShiroRealm();
}
/**
* session manager
*
* @param shiroRealm 授權認證
* @return 安全管理
*/
@Bean
@ConditionalOnClass(ShiroRealm.class)
public SecurityManager securityManager(ShiroRealm shiroRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(shiroRealm);
return securityManager;
}
/**
* Filter工廠,設置對應的過濾條件和跳轉條件
*
* @param securityManager session 管理
* @return shiro 過濾工廠
*/
@Bean
@ConditionalOnClass(value = {SecurityManager.class})
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
shiroFilterFactoryBean.setFilters(filterMap);
//URI過濾
Map map = Maps.newLinkedHashMap();
//可過濾的接口路徑
//所有API路徑進行校驗
map.put("/api/**",AUTHC_STR);
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
}
3.1 Shiro 過濾器小插曲
shiro和security也有相似之處,都有自己的 filter chain。翻一番Shiro的源碼,追溯一下。發下以下:
protected FilterChainManager createFilterChainManager() {
DefaultFilterChainManager manager = new DefaultFilterChainManager();
Map defaultFilters = manager.getFilters();
//apply global settings if necessary:
for (Filter filter : defaultFilters.values()) {
applyGlobalPropertiesIfNecessary(filter);
}
//Apply the acquired and/or configured filters:
Map filters = getFilters();
if (!CollectionUtils.isEmpty(filters)) {
for (Map.Entry entry : filters.entrySet()) {
String name = entry.getKey();
Filter filter = entry.getValue();
applyGlobalPropertiesIfNecessary(filter);
if (filter instanceof Nameable) {
((Nameable) filter).setName(name);
}
//"init" argument is false, since Spring-configured filters should be initialized
//in Spring (i.e. "init-method=blah") or implement InitializingBean:
manager.addFilter(name, filter, false);
}
}
//build up the chains:
Map chains = getFilterChainDefinitionMap();
if (!CollectionUtils.isEmpty(chains)) {
for (Map.Entry entry : chains.entrySet()) {
String url = entry.getKey();
String chainDefinition = entry.getValue();
manager.createChain(url, chainDefinition);
}
}
return manager;
}
從源碼可以發現,shiro的過濾器鏈,添加順序是:
defaultFilters: shiro默認的過濾器鏈
filters: 咱們自定義的過濾器鏈
chains:明確指定要過濾的
這里咱看看DefaultFilterChainManager 到底添加了那些默認過濾器鏈,可以看到主要的是:DefaultFilter
protected void addDefaultFilters(boolean init) {
for (DefaultFilter defaultFilter : DefaultFilter.values()) {
addFilter(defaultFilter.name(), defaultFilter.newInstance(), init, false);
}
}
anon(AnonymousFilter.class), authc(FormAuthenticationFilter.class), authcBasic(BasicHttpAuthenticationFilter.class), logout(LogoutFilter.class), noSessionCreation(NoSessionCreationFilter.class), perms(PermissionsAuthorizationFilter.class), port(PortFilter.class), rest(HttpMethodPermissionFilter.class), roles(RolesAuthorizationFilter.class), ssl(SslFilter.class), user(UserFilter.class);4. 測它
由于設置對全局接口進行校驗,因此,預期結果就是不能夠訪問啦
map.put("/api/**",AUTHC_STR);
4.1 IDAL
@RestController
@RequestMapping( SYSTEM_API +"shiro")
public class ShiroIdal {
@Resource
private IShiroService iShiroService;
@GetMapping
public HttpEntity obtain(@RequestParam String name){
return iShiroService.obtainUserByName(name);
}
}
4.2 service
@Slf4j
@Service
public class ShiroServiceImpl implements IShiroService {
@Resource
private ShiroPermissionRepository shiroPermissionRepository;
public HttpEntity obtainUserByName(String name) {
UserPO userPO = shiroPermissionRepository.findAllByName(name);
return HttpResponseSupport.success(userPO);
}
}
4.3 被劫持的情況
若沒 login.jsp,則會直接報錯,個人覺得太不和諧了,畢竟現在都是前后端分離的。
4.4 設置允許訪問在URI過濾Map加入以下:
map.put("/api/shiro",ANON_STR);
注意: 要在“全局Api劫持”前添加。而且不要使用“HashMap”,為什么?
在說為什么前,先了解HashMap這貨是什么原理先。
for (Entry entry : hashMap.entrySet()) {
MessageFormat.format("{0}={1}",entry.getKey(),entry.getValue());
}
HashMap散列圖是按“有利于隨機查找的散列(hash)的順序”。并非按輸入順序。遍歷時只能全部輸出,而沒有順序。甚至可以rehash()重新散列,來獲得更利于隨機存取的內部順序。
這會影響shiro哪里呢?
Map chains = getFilterChainDefinitionMap();
if (!CollectionUtils.isEmpty(chains)) {
for (Map.Entry entry : chains.entrySet()) {
String url = entry.getKey();
String chainDefinition = entry.getValue();
manager.createChain(url, chainDefinition);
}
}
ShiroFilterFactoryBean 中,在構建shiro的filter chain時,會對我們配置的FilterChainDefinitionMap 進行一次遍歷,并且將其添加到DefaultFilterChainManager中。
設想以下,若“全局API劫持”在最前面,那么只要在/api/*襠下的,都早早被劫持了。輪得到配置的 anon 么?若由于HashMap的散列排序導致“全局API劫持”在最前面,emmmm,那玩錘子。
因此,建議使用LinkedHashMap,為啥子?擼源碼
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry before, after;
Entry(int hash, K key, V value, Node next) {
super(hash, key, value, next);
}
}
transient LinkedHashMap.Entry head;
transient LinkedHashMap.Entry tail;
內部類中多了兩個Entry,一個記錄前方entry,一個記錄后方entry,這樣的雙向鏈表結構保證了插入順序的有序。
LinkedHashMap底層是數組加單項鏈表加雙向鏈表 。
數組加單向鏈表就是HashMap的結構,記錄數據用,
雙向鏈表,存儲插入順序用。
有點跑偏了,這些大伙肯定都知道滴了......
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/7193.html
摘要:此文章僅僅說明在整合時的一些坑并不是教程增加依賴集成依賴配置三個必須的用于授權和登錄創建自己的實例用于實現權限三種方式實現定義權限路徑第一種使用角色名定義第二種使用權限定義第三種使用接口的自定義配置此處配置之后需要在對應的 此文章僅僅說明在springboot整合shiro時的一些坑,并不是教程 增加依賴 org.apache.shiro shiro-spring-...
摘要:開公眾號差不多兩年了,有不少原創教程,當原創越來越多時,大家搜索起來就很不方便,因此做了一個索引幫助大家快速找到需要的文章系列處理登錄請求前后端分離一使用完美處理權限問題前后端分離二使用完美處理權限問題前后端分離三中密碼加鹽與中異常統一處理 開公眾號差不多兩年了,有不少原創教程,當原創越來越多時,大家搜索起來就很不方便,因此做了一個索引幫助大家快速找到需要的文章! Spring Boo...
摘要:框架具有輕便,開源的優點,所以本譯見構建用戶管理微服務五使用令牌和來實現身份驗證往期譯見系列文章在賬號分享中持續連載,敬請查看在往期譯見系列的文章中,我們已經建立了業務邏輯數據訪問層和前端控制器但是忽略了對身份進行驗證。 重拾后端之Spring Boot(四):使用JWT和Spring Security保護REST API 重拾后端之Spring Boot(一):REST API的搭建...
閱讀 1668·2021-11-16 11:44
閱讀 2404·2021-10-11 11:07
閱讀 4064·2021-10-09 09:41
閱讀 673·2021-09-22 15:52
閱讀 3195·2021-09-09 09:33
閱讀 2712·2019-08-30 15:55
閱讀 2291·2019-08-30 15:55
閱讀 843·2019-08-30 15:55