最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊

Shiro 使用 Token進(jìn)行認(rèn)證

2023-02-06 22:25 作者:Anyin灬  | 我要投稿

引言

在一些老項(xiàng)目中,可能使用shiro進(jìn)行權(quán)限認(rèn)證和校驗(yàn),而shiro是基于cookie/session的, 在現(xiàn)在前后端分離開發(fā)的場景下,前端開發(fā)人員需要在本地和后端進(jìn)行調(diào)試,勢必會(huì)遇到跨域的問題。 而現(xiàn)在隨著谷歌瀏覽器升級(jí),已經(jīng)禁止在跨域的情況下攜帶cookie。

所以,基于此背景,對(duì)老項(xiàng)目的shiro框架進(jìn)行一番改造,使得支持token的認(rèn)證方式,又不影響舊代碼。

Shiro 基本機(jī)制

在進(jìn)行Shiro 改造之前,首先我們得先了解下Shiro的基本機(jī)制。這里主要涉及到4個(gè)組件:

  • AuthenticationToken 身份驗(yàn)證的抽象接口,該接口會(huì)返回2個(gè)信息,用戶信息(Principal)和憑證信息(Credentials)

  • CredentilsMatcher 憑證校驗(yàn)接口,其實(shí)就是密碼的校驗(yàn)器

  • AuthenticatingFilter 身份驗(yàn)證過濾器,在請求過來的時(shí)候創(chuàng)建AuthenticationToken對(duì)象以及執(zhí)行登錄操作

  • AuthorizingRealm 授權(quán)接口,在該接口會(huì)獲取權(quán)限信息(doGetAuthorizationInfo)和用戶信息(doGetAuthenticationInfo)

基本流程如下:

image.png

改造思路

  1. 創(chuàng)建一個(gè)AuthenticationToken 接口的實(shí)現(xiàn)類,用于存放我們的Token信息

  2. 創(chuàng)建一個(gè)AuthenticatingFilter的實(shí)現(xiàn)類,這里我們需要做3件事

    • 當(dāng)前Ruequest獲取token,從而創(chuàng)建AuthenticationToken對(duì)象

    • onAccessDenied方法,校驗(yàn)Token的有效性

    • 最后執(zhí)行登錄操作,這里的登錄操作其實(shí)是用token換取用戶信息,會(huì)執(zhí)行AuthorizingRealmdoGetAuthenticationInfo方法

  3. 創(chuàng)建一個(gè)AuthorizingRealm的實(shí)現(xiàn)類,這里主要做3件事

    • 重寫supports方法,使得支持我們自定義的Token

    • 實(shí)現(xiàn)doGetAuthorizationInfo方法,這里是返回用戶的權(quán)限集合

    • 實(shí)現(xiàn)doGetAuthenticationInfo方法,這里我們根據(jù)Token獲取用戶信息

  4. 創(chuàng)建一個(gè)CredentilsMatcher接口的實(shí)現(xiàn)類,這里我們不對(duì)密碼進(jìn)行校驗(yàn),直接返回true。因?yàn)楫?dāng)你能拿到Token,證明賬號(hào)密碼已經(jīng)校驗(yàn)過。所以賬號(hào)密碼校驗(yàn)實(shí)際應(yīng)該是在業(yè)務(wù)層進(jìn)行校驗(yàn),校驗(yàn)通過之后才創(chuàng)建Token

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

  1. AuthenticationToken的實(shí)現(xiàn)類

public class ShiroToken implements AuthenticationToken {
? ?private String token;
? ?public ShiroToken(String token) {
? ? ? ?this.token = token;
? ?}
? ?@Override
? ?public Object getPrincipal() {
? ? ? ?return token;
? ?}
? ?@Override
? ?public Object getCredentials() {
? ? ? ?return token;
? ?}
}

  1. AuthenticatingFilter的實(shí)現(xiàn)類

@Slf4j
public class TokenFilter extends AuthenticatingFilter {

? ?private static final String X_TOKEN = "X-Token";

? ?private ITokenService tokenService = null;

? ?/**
? ? * 創(chuàng)建Token, 支持自定義Token
? ? */
? ?@Override
? ?protected AuthenticationToken createToken(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
? ? ? ?String token = this.getToken((HttpServletRequest)servletRequest);
? ? ? ?if(ObjectUtils.isEmpty(token)){
? ? ? ? ? ?log.error("token is empty");
? ? ? ? ? ?return null;
? ? ? ?}
? ? ? ?return new ShiroToken(token);
? ?}

? ?/**
? ? * 兼容跨域
? ? */
? ?@Override
? ?protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
? ? ? ?return ((HttpServletRequest) request).getMethod().equals(RequestMethod.OPTIONS.name());
? ?}

? ?@Override
? ?protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
? ? ? ?HttpServletRequest request = (HttpServletRequest)servletRequest;
? ? ? ?HttpServletResponse response = (HttpServletResponse) servletResponse;

? ? ? ?String token = this.getToken(request);
? ? ? ?if(ObjectUtils.isEmpty(token)){
? ? ? ? ? ?this.respUnLogin(request, response);
? ? ? ? ? ?return false;
? ? ? ?}

? ? ? ?// 校驗(yàn)Token的有效性
? ? ? ?if(tokenService == null){
? ? ? ? ? ?tokenService = SpringContext.getBean(ITokenService.class);
? ? ? ?}

? ? ? ?if(!tokenService.check(token)){
? ? ? ? ? ?this.respUnLogin(request, response);
? ? ? ?}

? ? ? ?// 根據(jù)token獲取用戶信息,會(huì)執(zhí)行 TokenRealm#doGetAuthenticationInfo 方法
? ? ? ?return executeLogin(servletRequest, servletResponse);
? ?}

? ?private void respUnLogin(HttpServletRequest request, HttpServletResponse response) throws IOException {
? ? ? ?response.setContentType("application/json;charset=utf-8");
? ? ? ?response.setHeader("Access-Control-Allow-Credentials", "true");
? ? ? ?response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));

? ? ? ?Response resp = new Response(BusinessCodeEnum.USER_UN_LOGIN.getCode(), BusinessCodeEnum.USER_UN_LOGIN.getMsg());
? ? ? ?response.getWriter().print(JSONUtil.toJsonStr(resp));
? ?}

? ?/**
? ? * 獲取token
? ? * 優(yōu)先從header獲取
? ? * 如果沒有,則從parameter獲取
? ? * @param request request
? ? * @return token
? ? */
? ?private String getToken(HttpServletRequest request){
? ? ? ?String token = request.getHeader(X_TOKEN);
? ? ? ?if(ObjectUtils.isEmpty(token)){
? ? ? ? ? ?token = request.getParameter(X_TOKEN);
? ? ? ?}
? ? ? ?return token;
? ?}
}

這里需要特別注意TokenFilterFilter的實(shí)現(xiàn)類,并不在Spring的容器中管理,所以無法通過@Autowire等注解進(jìn)行注入,只能通過構(gòu)造函數(shù)或者在使用的時(shí)候通過Spring的上下文中獲取。建議通過Spring的上下文中獲取,通過構(gòu)造器注入可能采坑。

  1. AuthorizingRealm的實(shí)現(xiàn)類

@Slf4j
public class TokenRealm extends AuthorizingRealm {

? ?@Autowired
? ?@Lazy
? ?private ITokenService tokenService;

? ?@Override
? ?public boolean supports(AuthenticationToken token) {
? ? ? ?return token instanceof ShiroToken;
? ?}

? ?@Override
? ?protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
? ? ? ?log.info("user do authorization: {}", principalCollection);
? ? ? ?return null;
? ?}

? ?@Override
? ?protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
? ? ? ?log.info("user do authentication: {}", authenticationToken);
? ? ? ?ShiroToken token = (ShiroToken)authenticationToken;
? ? ? ?UserInfo userInfo = tokenService.getUserInfo(token.getCredentials().toString());
? ? ? ?if(userInfo == null){
? ? ? ? ? ?throw BusinessCodeEnum.TOKEN_INVALID.getException();
? ? ? ?}
? ? ? ?return new SimpleAuthenticationInfo(userInfo.getUsername(), userInfo.getPassword(), userInfo.getNickName());
? ?}
}

  1. CredentilsMatcher的實(shí)現(xiàn)類

public class TokenCredentialsMatcher implements CredentialsMatcher {
? ?@Override
? ?public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
? ? ? ?return token instanceof ShiroToken;
? ?}
}

最后

通過以上的簡單改造,我們就可以實(shí)現(xiàn)基于Token的認(rèn)證方式又不需要改動(dòng)Shiro框架的其他功能了。

完整源碼地址:https://gitee.com/anyin/shiro-to-token


Shiro 使用 Token進(jìn)行認(rèn)證的評(píng)論 (共 條)

分享到微博請遵守國家法律
海晏县| 册亨县| 抚州市| 仁怀市| 纳雍县| 本溪市| 广州市| 平罗县| 澄江县| 荔浦县| 横峰县| 兴和县| 梅河口市| 平山县| 望奎县| 新竹市| 马尔康县| 桓台县| 新平| 邵东县| 抚顺县| 瓦房店市| 邹城市| 广灵县| 镶黄旗| 方正县| 扎囊县| 普陀区| 美姑县| 西丰县| 玉龙| 修水县| 宁南县| 赫章县| 开化县| 鲁甸县| 曲靖市| 高邑县| 沙田区| 商城县| 江口县|