b2b2c系統jwt權限源碼分享part2

在上一篇《b2b2c系統jwt權限源碼分享part1》中和大家分享了b2b2c系統中jwt權限的基礎設計及源碼,本文繼續和大家分享jwt和spring security整合部分的思路和源碼。

在上一篇文章中已經分享了關鍵的類圖:

b2b2c系統jwt權限源碼分享part2

如上圖所示,權限的校驗主要涉及到四個類:

  • AbstractAuthenticationService
  • BuyerAuthenticationService
  • SellerAuthenticationService
  • AdminAuthenticationService

AbstractAuthenticationService

對於三端(買家買家管理端)驗權的公用部分我們抽象在AbstractAuthenticationService中:

<code>public abstract class AbstractAuthenticationService implements AuthenticationService {

@Autowired
protected TokenManager tokenManager;


private final Logger logger = LoggerFactory.getLogger(getClass());

/**
* 單例模式的cache
*/
private static Cache<string> cache;


@Autowired
private JavashopConfig javashopConfig;


/**
* 鑑權,先獲取token,再根據token來鑑權
* 生產環境要由nonce和時間戳,簽名來獲取token
* 開發環境可以直接傳token

*
* @param req
*/
@Override
public void auth(HttpServletRequest req) {
String token = this.getToken(req);
if (StringUtil.notEmpty(token)) {
Authentication authentication = getAuthentication(token);
if (authentication != null) {
SecurityContextHolder.getContext().setAuthentication(authentication);
}

}
}

/**
* 接收用戶禁用或解禁事件

* 禁用:將被禁用的用戶id寫入緩存
* 解禁:將緩存中存放的用戶id刪除
*
* @param userDisableMsg
*/
@Override
public void userDisableEvent(UserDisableMsg userDisableMsg) {

//在緩存中記錄用戶被禁用
Cache<string> cache = this.getCache();

if (UserDisableMsg.ADD.equals(userDisableMsg.getOperation())) {
logger.debug("收到用戶禁用消息:" + userDisableMsg);
cache.put(getKey(userDisableMsg.getRole(), userDisableMsg.getUid()), 1);
}

if (UserDisableMsg.DELETE.equals(userDisableMsg.getOperation())) {
logger.debug("收到用戶解禁消息:" + userDisableMsg);
cache.remove(getKey(userDisableMsg.getRole(), userDisableMsg.getUid()), 1);
}
}

protected void checkUserDisable(Role role, int uid) {
Cache<string> cache = this.getCache();
Integer isDisable = cache.get(getKey(role, uid));
if (isDisable == null) {

return;
}
if (1 == isDisable) {
throw new RuntimeException("用戶已經被禁用");
}
}

private String getKey(Role role, int uid) {

return role.name() + "_" + uid;
}

/**
* 解析一個token
* 子類需要將token解析自己的子業務權限模型:Admin,seller buyer...
*
* @param token
* @return
*/
protected abstract AuthUser parseToken(String token);

/**
* 根據一個 token 生成授權
*
* @param token
* @return 授權
*/
protected Authentication getAuthentication(String token) {
try {

AuthUser user = parseToken(token);
List<grantedauthority> auths = new ArrayList<>();

List<string> roles = user.getRoles();

for (String role : roles) {
auths.add(new SimpleGrantedAuthority("ROLE_" + role));
}

UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken("user", null, auths);
authentication.setDetails(user);

return authentication;
} catch (Exception e) {
logger.error("認證異常", e);
return new UsernamePasswordAuthenticationToken("anonymous", null);

}
}

/**
* 獲取token
* 7.2.0起,廢棄掉重放攻擊的判斷
*
* @param req
* @return
*/
protected String getToken(HttpServletRequest req) {

String token = req.getHeader(TokenConstant.HEADER_STRING);
if (StringUtil.notEmpty(token)) {
token = token.replaceAll(TokenConstant.TOKEN_PREFIX, "").trim();
}

return token;
}

private static final Object lock = new Object();

/**
* 獲取本地緩存

* 用於記錄被禁用的用戶

* 此緩存的key為:角色+用戶id,如: admin_1
* value為:1則代表此用戶被禁用
*
* @return
*/
protected Cache<string> getCache() {

if (cache != null) {
return cache;
}
synchronized (lock) {
if (cache != null) {
return cache;
}
//緩存時間為session有效期+一分鐘
//也就表示,用戶如果被禁用,session超時這個cache也就不需要了:

//因為他需要重新登錄就可以被檢測出無效
int sessionTimeout = javashopConfig.getRefreshTokenTimeout() - javashopConfig.getAccessTokenTimeout() + 60;

//使用ehcache作為緩存
CachingProvider provider = Caching.getCachingProvider("org.ehcache.jsr107.EhcacheCachingProvider");
CacheManager cacheManager = provider.getCacheManager();

MutableConfiguration<string> configuration =
new MutableConfiguration<string>()
.setTypes(String.class, Integer.class)
.setStoreByValue(false)
.setExpiryPolicyFactory(CreatedExpiryPolicy.factoryOf(new Duration(TimeUnit.SECONDS, sessionTimeout)));

cache = cacheManager.createCache("userDisable", configuration);

return cache;
}
}

}/<string>/<string>/<string>/<string>/<grantedauthority>/<string>/<string>/<string>/<code>

在 javashop b2b2c系統中 禁用用戶要求該用戶立刻無法操作,這部分功能體現在

checkUserDisable方法中,思路是通過監聽redis消息將禁用用戶放在本地cache中(這裡採用的是EHCache。)

BuyerAuthenticationService

有了之前的代碼基礎,三端的權限校驗就比較簡單了:

<code>@Component
public class BuyerAuthenticationService extends AbstractAuthenticationService {

@Override
protected AuthUser parseToken(String token) {
AuthUser authUser= tokenManager.parse(Buyer.class, token);
User user = (User) authUser;

checkUserDisable(Role.BUYER, user.getUid());
return authUser;
}

}/<code>

SellerAuthenticationService

<code>@Component
public class SellerAuthenticationService extends AbstractAuthenticationService {

/**
* 將token解析為Clerk
*
* @param token
* @return
*/
@Override
protected AuthUser parseToken(String token) {
AuthUser authUser = tokenManager.parse(Clerk.class, token);
User user = (User) authUser;
checkUserDisable(Role.CLERK, user.getUid());
return authUser;
}

}/<code>

AdminAuthenticationService

<code>@Component
public class AdminAuthenticationService extends AbstractAuthenticationService {


/**
* 將token解析為Admin
* @param token
* @return
*/
@Override
protected AuthUser parseToken(String token) {

AuthUser authUser= tokenManager.parse(Admin.class, token);
User user = (User) authUser;
checkUserDisable(Role.ADMIN, user.getUid());
return authUser;

}

}/<code>

整合Security:

<code>@Configuration
@EnableWebSecurity
public class BuyerSecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
private DomainHelper domainHelper;

@Autowired
private BuyerAuthenticationService buyerAuthenticationService;

@Autowired
private AccessDeniedHandler accessDeniedHandler;

@Autowired
private AuthenticationEntryPoint authenticationEntryPoint;




/**
* 定義seller工程的權限
*
* @param http
* @throws Exception
*/
@Override
public void configure(HttpSecurity http) throws Exception {
http.cors().configurationSource((CorsConfigurationSource) ApplicationContextHolder.getBean("corsConfigurationSource")).and().csrf().disable()
//禁用session
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()

//定義驗權失敗返回格式
.exceptionHandling().accessDeniedHandler(accessDeniedHandler).authenticationEntryPoint(authenticationEntryPoint).and()
.authorizeRequests()
.and()
.addFilterBefore(new TokenAuthenticationFilter(buyerAuthenticationService),
UsernamePasswordAuthenticationFilter.class);

//過濾掉swagger的路徑
http.authorizeRequests().antMatchers("/v2/api-docs", "/configuration/ui", "/swagger-resources", "/configuration/security", "/swagger-ui.html", "/webjars/**").anonymous();

//過濾掉不需要買家權限的api
http.authorizeRequests().antMatchers("/debugger/**" ).permitAll().and();
//定義有買家權限才可以訪問
http.authorizeRequests().anyRequest().hasRole(Role.BUYER.name());
http.headers().addHeaderWriter(xFrameOptionsHeaderWriter());
//禁用緩存
http.headers().cacheControl().and()
.contentSecurityPolicy("script-src 'self' 'unsafe-inline' ; frame-ancestors " + domainHelper.getBuyerDomain());

}/<code>

以上就是javashop電商系統源碼中關於權限相關的分享。


易族智匯(javashop)原創文章


分享到:


相關文章: