摘要:雖然,直接用和進(jìn)行全家桶式的合作是最好不過的,但現(xiàn)實(shí)總是欺負(fù)我們這些沒辦法決定架構(gòu)類型的娃子。并非按輸入順序。遍歷時只能全部輸出,而沒有順序。設(shè)想以下,若全局劫持在最前面,那么只要在襠下的,都早早被劫持了。底層是數(shù)組加單項(xiàng)鏈表加雙向鏈表。
1. 添加Shiro依賴雖然,直接用Spring Security和SpringBoot 進(jìn)行“全家桶式”的合作是最好不過的,但現(xiàn)實(shí)總是欺負(fù)我們這些沒辦法決定架構(gòu)類型的娃子。
Apache Shiro 也有其特殊之處滴。若需了解,可以轉(zhuǎn)戰(zhàn)到[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
授權(quán)認(rèn)證具體實(shí)現(xiàn)之地。通過繼承 AuthorizingRealm 進(jìn)而實(shí)現(xiàn),對登錄時的賬號密碼校驗(yàn)功能
@Slf4j
public class ShiroRealm extends AuthorizingRealm {
@Autowired
private ShiroPermissionRepository shiroPermissionRepository;
/**
* 授權(quán)
*
* @param principalCollection 主要信息
* @return 授權(quán)信息
*/
@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;
}
/**
* 認(rèn)證
*
* @param authenticationToken 認(rèn)證token
* @return 認(rèn)證結(jié)果
* @throws AuthenticationException 認(rèn)證異常
*/
@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();
//校驗(yàn)用戶名
checkBlank(principal,"用戶名不能為空");
//校驗(yàn)密碼
checkBlank(credentials,"密碼不能為空");
//校驗(yàn)姓名
String username = (String) principal;
UserPO userPO = shiroPermissionRepository.findAllByName(username);
if (userPO == null){
throw new AccountException("用戶名錯誤");
}
//校驗(yàn)密碼
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";
/**
* 驗(yàn)證授權(quán)、認(rèn)證
*
* @return shiroRealm 授權(quán)認(rèn)證
*/
@Bean
public ShiroRealm shiroRealm(){
return new ShiroRealm();
}
/**
* session manager
*
* @param shiroRealm 授權(quán)認(rèn)證
* @return 安全管理
*/
@Bean
@ConditionalOnClass(ShiroRealm.class)
public SecurityManager securityManager(ShiroRealm shiroRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(shiroRealm);
return securityManager;
}
/**
* Filter工廠,設(shè)置對應(yīng)的過濾條件和跳轉(zhuǎn)條件
*
* @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路徑進(jìn)行校驗(yàn)
map.put("/api/**",AUTHC_STR);
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
}
3.1 Shiro 過濾器小插曲
shiro和security也有相似之處,都有自己的 filter chain。翻一番Shiro的源碼,追溯一下。發(fā)下以下:
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;
}
從源碼可以發(fā)現(xiàn),shiro的過濾器鏈,添加順序是:
defaultFilters: shiro默認(rèn)的過濾器鏈
filters: 咱們自定義的過濾器鏈
chains:明確指定要過濾的
這里咱看看DefaultFilterChainManager 到底添加了那些默認(rèn)過濾器鏈,可以看到主要的是: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. 測它
由于設(shè)置對全局接口進(jìn)行校驗(yàn),因此,預(yù)期結(jié)果就是不能夠訪問啦
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,則會直接報(bào)錯,個人覺得太不和諧了,畢竟現(xiàn)在都是前后端分離的。
4.4 設(shè)置允許訪問在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散列圖是按“有利于隨機(jī)查找的散列(hash)的順序”。并非按輸入順序。遍歷時只能全部輸出,而沒有順序。甚至可以rehash()重新散列,來獲得更利于隨機(jī)存取的內(nèi)部順序。
這會影響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 中,在構(gòu)建shiro的filter chain時,會對我們配置的FilterChainDefinitionMap 進(jìn)行一次遍歷,并且將其添加到DefaultFilterChainManager中。
設(shè)想以下,若“全局API劫持”在最前面,那么只要在/api/*襠下的,都早早被劫持了。輪得到配置的 anon 么?若由于HashMap的散列排序?qū)е隆叭諥PI劫持”在最前面,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;
內(nèi)部類中多了兩個Entry,一個記錄前方entry,一個記錄后方entry,這樣的雙向鏈表結(jié)構(gòu)保證了插入順序的有序。
LinkedHashMap底層是數(shù)組加單項(xiàng)鏈表加雙向鏈表 。
數(shù)組加單向鏈表就是HashMap的結(jié)構(gòu),記錄數(shù)據(jù)用,
雙向鏈表,存儲插入順序用。
有點(diǎn)跑偏了,這些大伙肯定都知道滴了......
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/7193.html
摘要:此文章僅僅說明在整合時的一些坑并不是教程增加依賴集成依賴配置三個必須的用于授權(quán)和登錄創(chuàng)建自己的實(shí)例用于實(shí)現(xiàn)權(quán)限三種方式實(shí)現(xiàn)定義權(quán)限路徑第一種使用角色名定義第二種使用權(quán)限定義第三種使用接口的自定義配置此處配置之后需要在對應(yīng)的 此文章僅僅說明在springboot整合shiro時的一些坑,并不是教程 增加依賴 org.apache.shiro shiro-spring-...
摘要:開公眾號差不多兩年了,有不少原創(chuàng)教程,當(dāng)原創(chuàng)越來越多時,大家搜索起來就很不方便,因此做了一個索引幫助大家快速找到需要的文章系列處理登錄請求前后端分離一使用完美處理權(quán)限問題前后端分離二使用完美處理權(quán)限問題前后端分離三中密碼加鹽與中異常統(tǒng)一處理 開公眾號差不多兩年了,有不少原創(chuàng)教程,當(dāng)原創(chuàng)越來越多時,大家搜索起來就很不方便,因此做了一個索引幫助大家快速找到需要的文章! Spring Boo...
摘要:框架具有輕便,開源的優(yōu)點(diǎn),所以本譯見構(gòu)建用戶管理微服務(wù)五使用令牌和來實(shí)現(xiàn)身份驗(yàn)證往期譯見系列文章在賬號分享中持續(xù)連載,敬請查看在往期譯見系列的文章中,我們已經(jīng)建立了業(yè)務(wù)邏輯數(shù)據(jù)訪問層和前端控制器但是忽略了對身份進(jìn)行驗(yàn)證。 重拾后端之Spring Boot(四):使用JWT和Spring Security保護(hù)REST API 重拾后端之Spring Boot(一):REST API的搭建...
閱讀 1649·2021-11-16 11:44
閱讀 2393·2021-10-11 11:07
閱讀 4036·2021-10-09 09:41
閱讀 663·2021-09-22 15:52
閱讀 3187·2021-09-09 09:33
閱讀 2701·2019-08-30 15:55
閱讀 2284·2019-08-30 15:55
閱讀 837·2019-08-30 15:55