前后端分离shiro集成未登录未授权option预检解决

        最近小编在做新项目的时候采用了前后端分离,使用shiro做权限控制,其中遇到了一些问题。

         一 :OPTION请求预检问题

        二:未登录未授权跳转问题

解决方案:

        一:放行OPTIONS请求

        二:继承FormAuthenticationFilter类重写其onAccessDenied方法解决未登录

        三:未授权设置全局异常捕获为AuthorizationException

        话不多多说直接上代码

package com.lepu.config;

import com.alibaba.fastjson.JSON;
import com.lepu.model.Constant;
import com.lepu.model.ResultBean;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;

/**
 * @author 闫鹏程
 * @date 2021-07-05 10:07
 * @description
 */
@Slf4j
public class ShiroFormAuthenticationFilter extends FormAuthenticationFilter {

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        if (this.isLoginRequest(request, response)) {
            if (this.isLoginSubmission(request, response)) {
                if (log.isTraceEnabled()) {
                    log.trace("Login submission detected.  Attempting to execute login.");
                }

                return this.executeLogin(request, response);
            } else {
                if (log.isTraceEnabled()) {
                    log.trace("Login page view.");
                }
                return true;
            }
        } else {
            HttpServletRequest req = (HttpServletRequest)request;
            HttpServletResponse resp = (HttpServletResponse)response;
            if (req.getMethod().equals(RequestMethod.OPTIONS.name())) {
                //放行OPTIONS预检请求
                resp.setStatus(HttpStatus.OK.value());
                return true;
            } else {
                if (log.isTraceEnabled()) {
                    log.trace("Attempting to access a path which requires authentication.  Forwarding to the Authentication url [{}]" ,this.getLoginUrl());
                }
                /**
                 * 在这里实现自己想返回的信息,其他地方和源码一样就可以了
                 */
                resp.setHeader("Access-Control-Allow-Origin", req.getHeader("Origin"));
                resp.setHeader("Access-Control-Allow-Credentials", "true");
                resp.setContentType("application/json; charset=utf-8");
                resp.setCharacterEncoding("UTF-8");
                PrintWriter out = resp.getWriter();
                out.println(JSON.toJSONString(ResultBean.reponse(Constant.ERROR_401)));
                out.flush();
                out.close();
                return false;
            }
        }
    }

}

shiro配置自定义filter

public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
    //1.创建过滤器工厂
    ShiroFilterFactoryBean filterFactory = new ShiroFilterFactoryBean();
    //2.设置安全管理器
    filterFactory.setSecurityManager(securityManager);
    //3.通用配置(跳转登录页面,未授权跳转的页面)
    //filterFactory.setLoginUrl("/autherror?code=1");//跳转url地址
    //filterFactory.setUnauthorizedUrl("/autherror?code=2");//未授权的url
    //4.设置过滤器集合
    Map<String,String> filterMap = new LinkedHashMap<>();
    //anon -- 匿名访问
    filterMap.put("/user/login","anon");
    /*filterMap.put("/sys/city/**","anon");
    filterMap.put("/sys/faceLogin/**","anon");*/
    filterMap.put("/autherror","anon");
    //注册
    //authc -- 认证之后访问(登录)
    filterMap.put("/**","authc");
    //perms -- 具有某中权限 (使用注解配置授权)
    filterFactory.setFilterChainDefinitionMap(filterMap);
    Map<String, Filter> filters = filterFactory.getFilters();
    //设置自定义filter
    filters.put("authc",new ShiroFormAuthenticationFilter());
    filterFactory.setFilters(filters);
    return filterFactory;
}

全局异常捕获设置

@ExceptionHandler(value = AuthorizationException.class)
@ResponseBody
public ResultBean error(HttpServletRequest request, HttpServletResponse response, AuthorizationException e) {
    return ResultBean.reponse(Constant.ERROR_402);
}

登录接口

@RequestMapping(value="/login",method = RequestMethod.POST)
public ResultBean login(@RequestBody Map<String,String> loginMap) {
    String userName = loginMap.get("UserName");
    String password = loginMap.get("Password");
    try {
        User user = userService.findByName(userName);
        if(user == null || user.getIsDelete() == 1){
            return ResultBean.reponse(Constant.SUCCESS_202);
        }
        if(user.getStatus()==1){
            return ResultBean.error("该用户已禁用");
        }
        //1.构造登录令牌 UsernamePasswordToken
        //加密密码
        password = new Md5Hash(password, userName, 1).toString();  //1.密码,盐,加密次数
        UsernamePasswordToken upToken = new UsernamePasswordToken(userName, password);
        //2.获取subject
        Subject subject = SecurityUtils.getSubject();
        //3.获取sessionId
        String sessionId = (String) subject.getSession().getId();
        redisUtil.incr(userName+ Permanent.LOGINNUM_PREFIX,1);
         int loginNum = (int)redisUtil.get(userName+Permanent.LOGINNUM_PREFIX);
        if(loginNum == 1){
            redisUtil.expire(userName+Permanent.LOGINNUM_PREFIX,5*60);
        }
        if(loginNum >= 10){
            userDao.updateStatus(new User(){{this.setStatus(2);this.setUserName(userName);}});
            redisUtil.expire(userName+Permanent.LOGINNUM_PREFIX,30*60);
            return ResultBean.error("205","5分钟内密码输入错误10次,账号锁定30分钟");
        }
        //4.调用login方法,进入realm完成认证
        subject.login(upToken);
        userDao.updateStatus(new User(){{this.setStatus(0);this.setUserName(userName);}});
        redisUtil.del(userName+Permanent.LOGINNUM_PREFIX);
        UserPrincipal principal = (UserPrincipal) subject.getPrincipal();
        //5.构造返回结果
        return ResultBean.ok("登录成功", new HashMap<String,Object>(){{this.put("Authorization",sessionId);this.put("user",principal);}});
    }catch (AuthenticationException e){
        //密码错误异常
        return ResultBean.reponse(Constant.SUCCESS_203);
    } catch (Exception e) {
        log.error("登录报错",e);
        return ResultBean.reponse(Constant.SUCCESS_203);
    }
}

如有不明白之处欢迎留言。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值