springboot結(jié)合oauth2實(shí)現(xiàn)權(quán)限認(rèn)證

1.什么是JWT

Json web token (JWT), 是為了在網(wǎng)絡(luò)應(yīng)用環(huán)境間傳遞聲明而執(zhí)行的一種基于JSON的開(kāi)放標(biāo)準(zhǔn)((RFC 7519).該token被設(shè)計(jì)為緊湊且安全的,特別適用于分布式站點(diǎn)的單點(diǎn)登錄(SSO)場(chǎng)景。JWT的聲明一般被用來(lái)在身份提供者和服務(wù)提供者間傳遞被認(rèn)證的用戶(hù)身份信息,以便于從資源服務(wù)器獲取資源,也可以增加一些額外的其它業(yè)務(wù)邏輯所必須的聲明信息,該token也可直接被用于認(rèn)證,也可被加密。

2.優(yōu)點(diǎn)

體積小、傳輸快

支持跨域授權(quán),因?yàn)榭缬驘o(wú)法共享cookie

分布式系統(tǒng)中,很好地解決了單點(diǎn)登錄問(wèn)題

3.使用場(chǎng)景

1. 認(rèn)證,這是比較常見(jiàn)的使用場(chǎng)景,只要用戶(hù)登錄過(guò)一次系統(tǒng),之后的請(qǐng)求都會(huì)包含簽名出來(lái)的token,通過(guò)token也可以用來(lái)實(shí)現(xiàn)單點(diǎn)登錄。

2. 交換信息,通過(guò)使用密鑰對(duì)來(lái)安全的傳送信息,可以知道發(fā)送者是誰(shuí)、放置消息被篡改。

4.項(xiàng)目克隆地址(以下過(guò)程代碼簡(jiǎn)寫(xiě),可自行克隆代碼查看實(shí)現(xiàn)過(guò)程,這個(gè)框架是我自己搭建的,里邊已經(jīng)集成了很多東西,喜歡的可以研究研究,都是比較常用的技術(shù),有什么問(wèn)題也可以評(píng)論,看到會(huì)回復(fù),賬號(hào)密碼 admin? ?admin)

ssh://admin@121.36.107.248:29418/goudan.git

5.集成過(guò)程

(1)導(dǎo)入sql表,我把sql文件放在下圖所示位置

sql文件

(2)添加依賴(lài)

依賴(lài)

(3)主體有5個(gè)文件需要添加,分別是shiroConfig、OAuth2Filer配置、OAuth2Realm、OAuth2Token、TokenGenerator

3.1?shiroConfig

shiroConfig

具體代碼如下

/**

* @program: xiaowu

* @description: Shiro配置

* @author: Wu

* @create: 2020-08-29 09:46

**/

@Configuration

public class ShiroConfig {

@Bean("sessionManager")

public SessionManagersessionManager(){

DefaultWebSessionManager sessionManager =new DefaultWebSessionManager();

? ? ? ? sessionManager.setSessionValidationSchedulerEnabled(true);

? ? ? ? sessionManager.setSessionIdCookieEnabled(true);

? ? ? ? return sessionManager;

? ? }

@Bean("securityManager")

public SecurityManagersecurityManager(OAuth2Realm oAuth2Realm, SessionManager sessionManager) {

DefaultWebSecurityManager securityManager =new DefaultWebSecurityManager();

? ? ? ? securityManager.setRealm(oAuth2Realm);

? ? ? ? securityManager.setSessionManager(sessionManager);

? ? ? ? return securityManager;

? ? }

@Bean("shiroFilter")

public ShiroFilterFactoryBeanshirFilter(SecurityManager securityManager) {

ShiroFilterFactoryBean shiroFilter =new ShiroFilterFactoryBean();

? ? ? ? shiroFilter.setSecurityManager(securityManager);

? ? ? ? //oauth過(guò)濾

? ? ? ? Map filters =new HashMap<>();

? ? ? ? filters.put("oauth2", new OAuth2Filter());

? ? ? ? shiroFilter.setFilters(filters);

? ? ? ? Map filterMap =new LinkedHashMap<>();

? ? ? ? filterMap.put("/druid/**", "anon");

? ? ? ? filterMap.put("/app/**", "anon");

? ? ? ? filterMap.put("/login", "anon");

? ? ? ? filterMap.put("/**", "oauth2");

? ? ? ? shiroFilter.setFilterChainDefinitionMap(filterMap);

? ? ? ? return shiroFilter;

? ? }

@Bean("lifecycleBeanPostProcessor")

public LifecycleBeanPostProcessorlifecycleBeanPostProcessor() {

return new LifecycleBeanPostProcessor();

? ? }

@Bean

? ? public DefaultAdvisorAutoProxyCreatordefaultAdvisorAutoProxyCreator() {

DefaultAdvisorAutoProxyCreator proxyCreator =new DefaultAdvisorAutoProxyCreator();

? ? ? ? proxyCreator.setProxyTargetClass(true);

? ? ? ? return proxyCreator;

? ? }

@Bean

? ? public AuthorizationAttributeSourceAdvisorauthorizationAttributeSourceAdvisor(SecurityManager securityManager) {

AuthorizationAttributeSourceAdvisor advisor =new AuthorizationAttributeSourceAdvisor();

? ? ? ? advisor.setSecurityManager(securityManager);

? ? ? ? return advisor;

? ? }

}

3.2? OAuth2Filer? 過(guò)濾器(oauth2是一種規(guī)范,springcloud有個(gè)springsecurity oauth2是實(shí)現(xiàn),這里用的jwt)

OAuth2Filer

具體代碼如下

/**

* @program: xiaowu

* @description: oauth2過(guò)濾器

* @author: Wu

* @create: 2020-08-29 10:09

**/

public class OAuth2Filterextends AuthenticatingFilter {

@Override

? ? protected AuthenticationTokencreateToken(ServletRequest request, ServletResponse response)throws Exception {

//獲取請(qǐng)求token

? ? ? ? String token = getRequestToken((HttpServletRequest) request);

? ? ? ? if(StringUtil.isBlank(token)){

return null;

? ? ? ? }

return new OAuth2Token(token);

? ? }

@Override

? ? protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {

if(((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name())){

return true;

? ? ? ? }

return false;

? ? }

@Override

? ? protected boolean onAccessDenied(ServletRequest request, ServletResponse response)throws Exception {

//獲取請(qǐng)求token,如果token不存在,直接返回401

? ? ? ? HttpServletRequest httpServletRequest = (HttpServletRequest) request;

? ? ? ? String token = getRequestToken((HttpServletRequest) request);

? ? ? ? if(StringUtil.isBlank(token)){

HttpServletResponse httpResponse = (HttpServletResponse) response;

? ? ? ? ? ? httpResponse.setHeader("Access-Control-Allow-Credentials", "true");

? ? ? ? ? ? httpResponse.setHeader("Access-Control-Allow-Origin", httpServletRequest.getHeader("Origin"));

? ? ? ? ? ? JSONObject json =new JSONObject();

? ? ? ? ? ? json.put("code", "401");

? ? ? ? ? ? json.put("msg", "invalid token");

? ? ? ? ? ? httpResponse.getWriter().print(json);

return false;

? ? ? ? }

return executeLogin(request, response);

? ? }

@Override

? ? protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {

HttpServletResponse httpResponse = (HttpServletResponse) response;

? ? ? ? HttpServletRequest httpServletRequest = (HttpServletRequest) request;

? ? ? ? httpResponse.setContentType("application/json;charset=utf-8");

? ? ? ? httpResponse.setHeader("Access-Control-Allow-Credentials", "true");

? ? ? ? httpResponse.setHeader("Access-Control-Allow-Origin", httpServletRequest.getHeader("Origin"));

? ? ? ? try {

//處理登錄失敗的異常

? ? ? ? ? ? Throwable throwable = e.getCause() ==null ? e : e.getCause();

? ? ? ? ? ? JSONObject json =new JSONObject();

? ? ? ? ? ? json.put("code", "401");

? ? ? ? ? ? json.put("msg", throwable.getMessage());

? ? ? ? ? ? httpResponse.getWriter().print(json);

? ? ? ? }catch (IOException e1) {

}

return false;

? ? }

/**

? ? * 獲取請(qǐng)求的token

*/

? ? private StringgetRequestToken(HttpServletRequest httpRequest){

//從header中獲取token

? ? ? ? String token = httpRequest.getHeader("token");

? ? ? ? //如果header中不存在token,則從參數(shù)中獲取token

? ? ? ? if(StringUtil.isBlank(token)){

token = httpRequest.getParameter("token");

? ? ? ? }

return token;

? ? }

}

3.3 OAuth2Realm配置(這個(gè)里面可以設(shè)置角色、權(quán)限和認(rèn)證信息)

OAuth2Realm

具體代碼如下

/**

* @program: xiaowu

* @description: 認(rèn)證

* @author: Wu

* @create: 2020-08-29 10:18

**/

@Component

public class OAuth2Realmextends AuthorizingRealm {

@Autowired

? ? private ManagerServicemanagerService;

? ? @Override

? ? public boolean supports(AuthenticationToken token) {

return tokeninstanceof OAuth2Token;

? ? }

/**

? ? * 授權(quán)(驗(yàn)證權(quán)限時(shí)調(diào)用, 控制role 和 permissins時(shí)使用)

*/

? ? @Override

? ? protected AuthorizationInfodoGetAuthorizationInfo(PrincipalCollection principals) {

ManagerInfo manager = (ManagerInfo)principals.getPrimaryPrincipal();

? ? ? ? Integer managerId = manager.getManagerId();

? ? ? ? SimpleAuthorizationInfo info =new SimpleAuthorizationInfo();

? ? ? ? // 模擬權(quán)限和角色

? ? ? ? Set permsSet =new HashSet<>();

? ? ? ? Set roles =new HashSet<>();

? ? ? ? if (managerId ==1) {

// 超級(jí)管理員-權(quán)限

? ? ? ? ? ? permsSet.add("delete");

? ? ? ? ? ? permsSet.add("update");

? ? ? ? ? ? permsSet.add("view");

? ? ? ? ? ? roles.add("admin");

? ? ? ? }else {

// 普通管理員-權(quán)限

? ? ? ? ? ? permsSet.add("view");

? ? ? ? ? ? roles.add("test");

? ? ? ? }

info.setStringPermissions(permsSet);

? ? ? ? info.setRoles(roles);

? ? ? ? return info;

? ? }

/**

? ? * 認(rèn)證(登錄時(shí)調(diào)用)

*/

? ? @Override

? ? protected AuthenticationInfodoGetAuthenticationInfo(AuthenticationToken token)throws AuthenticationException {

String accessToken = (String) token.getPrincipal();

? ? ? ? //根據(jù)accessToken,查詢(xún)用戶(hù)信息

? ? ? ? ManagerToken managerToken =managerService.queryByToken(accessToken);

? ? ? ? //token失效

? ? ? ? SimpleDateFormat sm =new SimpleDateFormat("yyyyMMddHHmmss");

? ? ? ? Date expireTime;

? ? ? ? boolean flag =true;

? ? ? ? try {

expireTime? ? = sm.parse(managerToken.getExpireTime());

? ? ? ? ? ? flag = managerToken ==null || expireTime.getTime() < System.currentTimeMillis();

? ? ? ? }catch (ParseException e) {

e.printStackTrace();

? ? ? ? }

if(flag){

throw new IncorrectCredentialsException("token失效,請(qǐng)重新登錄");

? ? ? ? }

//查詢(xún)用戶(hù)信息

? ? ? ? ManagerInfo managerInfo =managerService.getManagerInfo(managerToken.managerId);

? ? ? ? //賬號(hào)鎖定

? ? ? ? // if(managerInfo.getStatus() == 0){

? ? ? ? //? ? throw new LockedAccountException("賬號(hào)已被鎖定,請(qǐng)聯(lián)系管理員");

// }

? ? ? ? SimpleAuthenticationInfo info =new SimpleAuthenticationInfo(managerInfo, accessToken, getName());

? ? ? ? return info;

? ? }

}

3.4?OAuth2Token設(shè)置

OAuth2Token

3.5?TokenGenerator, 生成token

TokenGenerator

具體代碼

/**

* @program: xiaowu

* @description: 生成token

* @author: Wu

* @create: 2020-08-29 10:26

**/

public class TokenGenerator {

public static StringgenerateValue() {

return generateValue(UUID.randomUUID().toString());

? ? }

private static final char[]hexCode ="0123456789abcdef".toCharArray();

? ? public static StringtoHexString(byte[] data) {

if (data ==null) {

return null;

? ? ? ? }

StringBuilder r =new StringBuilder(data.length *2);

? ? ? ? for (byte b : data) {

r.append(hexCode[(b >>4) &0xF]);

? ? ? ? ? ? r.append(hexCode[(b &0xF)]);

? ? ? ? }

return r.toString();

? ? }

public static StringgenerateValue(String param) {

try {

MessageDigest algorithm = MessageDigest.getInstance("MD5");

? ? ? ? ? ? algorithm.reset();

? ? ? ? ? ? algorithm.update(param.getBytes());

? ? ? ? ? ? byte[] messageDigest = algorithm.digest();

? ? ? ? ? ? return toHexString(messageDigest);

? ? ? ? }catch (Exception e) {

throw new RuntimeException("生成Token失敗", e);

? ? ? ? }

}

}

4.測(cè)試(這里以登錄接口驗(yàn)證為例)

登錄接口

實(shí)現(xiàn)層代碼

實(shí)現(xiàn)層

mapper文件還有這種寫(xiě)法(頭一次見(jiàn),以前都是在業(yè)務(wù)代碼里寫(xiě)的)

xml文件

postman測(cè)試

測(cè)試

mysql

接口權(quán)限已經(jīng)完成,剩下的就是根據(jù)過(guò)期時(shí)間的業(yè)務(wù)代碼,當(dāng)然這個(gè)token加密算法比較簡(jiǎn)單,我們可以更換加密算法,這以后再完善

喜歡請(qǐng)關(guān)注 “蛋皮皮” 微信公眾號(hào)!更多干貨等你來(lái)學(xué)習(xí)哦。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容