简易使用shrio 实现 登录验证

1、shiro 配置

import java.util.HashMap;
import java.util.Map;
import javax.servlet.Filter;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

@Configuration
public class ShiroConfig {

  @Bean("securityManager")
  public DefaultWebSecurityManager webSecurityManager(MyRealm myRealm) {
    DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
    //使用自己的realm
    manager.setRealm(myRealm);
    //关闭shiro自带的session
    DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
    DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
    defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
    subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
    manager.setSubjectDAO(subjectDAO);
    return manager;
  }

  @Bean("shiroFilter")
  public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
    ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
    //添加自己的过滤器并取名为jwt
    Map filterMap = new HashMap<>();
    filterMap.put("jwt", new JWTFilter());
    factoryBean.setFilters(filterMap);
    factoryBean.setSecurityManager(securityManager);
    factoryBean.setUnauthorizedUrl("/401");

    //自定义url规则
    Map filterRuleMap = new HashMap<>();
    filterRuleMap.put("/**", "jwt");
    // 跳过对swagger拦截
    filterRuleMap.put("/swagger-resources/**", "anon");
    filterRuleMap.put("/webjars/**", "anon");
    filterRuleMap.put("/v2/**", "anon");
    filterRuleMap.put("/swagger-ui.html/**", "anon");

    filterRuleMap.put("/login/**", "anon");
    filterRuleMap.put("/401", "anon");
    factoryBean.setFilterChainDefinitionMap(filterRuleMap);
    return factoryBean;
  }

  /**
   * 添加注解支持
   */
  @Bean
  @DependsOn("lifecycleBeanPostProcessor")
  public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
    DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
    //强制使用cglib代理
    defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
    return defaultAdvisorAutoProxyCreator;
  }

  @Bean
  public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
    return new LifecycleBeanPostProcessor();
  }

  @Bean
  public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
      DefaultWebSecurityManager securityManager) {
    AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
    advisor.setSecurityManager(securityManager);
    return advisor;
  }
}

 2、登录的Realm

controller只负责封装下参数,然后扔给Shiro了,这时候Shiro收到后,会到所有的realm中找能处理UsernamePasswordToken的Realm

import com.yzf.enterprise.app.risk.core.utils.JWTUtil;
import com.yzf.enterprise.app.risk.model.entity.OperateUserEntity;
import com.yzf.enterprise.app.risk.service.ActionService;
import com.yzf.enterprise.app.risk.service.UserService;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class MyRealm extends AuthorizingRealm {

  private static final Logger logger = LoggerFactory.getLogger(MyRealm.class);

  @Autowired
  private UserService userService;
  @Autowired
  private ActionService actionService;

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

  /**
   * 当需要检测用户权限的时候才会调用此方法,例如checkRole,checkPermission之类的
   */
  @Override
  protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    JWTToken jwtToken = (JWTToken) getAvailablePrincipal(principals);
    // 权限认证 无权限就异常处理
    if (!actionService.checkUrlAction(jwtToken.getUrl(),
        Long.valueOf(JWTUtil.getValue(jwtToken.getToken(), "userId")))) {
      throw new AuthenticationException("用户无权限");
    }
    SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
    Set set = new HashSet<>();
    set.add(jwtToken.getUrl());
    simpleAuthorizationInfo.setStringPermissions(set);
    return simpleAuthorizationInfo;
  }

  /**
   * 默认使用此方法进行用户名正确与否验证,错误抛出异常即可。
   */
  @Override
  protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth)
      throws AuthenticationException {
    JWTToken jwtToken = (JWTToken) auth;
    String token = jwtToken.getToken();
    // 解密获得userId,用于和数据库进行对比
    String userId = JWTUtil.getValue(token, "userId");
    if (StringUtils.isBlank(userId)) {
      throw new AuthenticationException("token invalid");
    }
    OperateUserEntity operateUserEntity = userService.selectByKey(Long.valueOf(userId));
    if (operateUserEntity == null) {
      throw new AuthenticationException("用户不存在,userId=" + userId);
    }
    // 验证token 是否正确
    if (!JWTUtil.verify(token, "userId", userId, operateUserEntity.getPassword())) {
      throw new AuthenticationException("用户信息异常");
    }
    return new SimpleAuthenticationInfo(jwtToken, jwtToken, this.getName());
  }
}

 3、设置 自己的filter   

import com.alibaba.fastjson.JSON;
import com.yunzhangfang.common.utils.AjaxResult;
import com.yzf.enterprise.app.risk.base.constant.GlobalProperties;
import com.yzf.enterprise.app.risk.core.constant.RiskConstants.OperateShiro;
import com.yzf.enterprise.app.risk.core.enums.ResultCodeEnum;
import com.yzf.enterprise.app.risk.core.support.Json;
import java.io.IOException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMethod;

public class JWTFilter extends BasicHttpAuthenticationFilter {

  private static final Logger logger = LoggerFactory.getLogger(JWTFilter.class);

  /**
   * 判断用户是否想要登入
   */
  @Override
  protected boolean isLoginAttempt(ServletRequest request, ServletResponse response) {
    return true;
  }

  /**
   * 验证登陆
   */
  @Override
  protected boolean executeLogin(ServletRequest request, ServletResponse response)
      throws Exception {
    JWTToken jwtToken = analyzeJWTToken(request);
    // 提交给realm进行登入,如果错误会抛出异常并被捕获
    getSubject(request, response).login(jwtToken);
    // 如果没有抛出异常则代表登入成功
    return true;
  }

  /**
   * 权限认证
   */
  @Override
  protected boolean isPermissive(Object mappedValue) {
    return SecurityUtils.getSubject().isPermitted(String.valueOf(mappedValue));
  }

  /**
   * 是否允许继续访问
   */
  @Override
  protected boolean isAccessAllowed(ServletRequest request, ServletResponse response,
      Object mappedValue) {
    // 跳过开发环境
    if ("dev".equals(GlobalProperties.profiles) || "test-local".equals(GlobalProperties.profiles)) {
      return true;
    }
    try {
      // 验证登录
      executeLogin(request, response);
      // 验证权限
      isPermissive(analyzeJWTToken(request).getUrl());
      return true;
    } catch (Exception e) {
      logger.error("token验证异常:" + e, e);
      return false;
    }
  }

  /**
   * 如果Shiro Login认证成功,会进入该方法,等同于用户名密码登录成功,我们这里还判断了是否要刷新Token
   */
  @Override
  protected boolean onLoginSuccess(AuthenticationToken token, Subject subject,
      ServletRequest request, ServletResponse response) throws Exception {
    issueSuccessRedirect(request, response);
    return true;
  }

  /**
   * 拒绝访问之后 是否需要继续处理
   */
  @Override
  protected boolean onAccessDenied(ServletRequest request, ServletResponse res, Object mappedValue)
      throws IOException {
    logger.info("token认证失败");
    AjaxResult ajaxResult = AjaxResult.failed(ResultCodeEnum.UNLOGIG);
    HttpServletResponse response = (HttpServletResponse) res;
    response.setContentType("application/json;charset=UTF-8");
    response.getWriter().write(JSON.toJSONString(ajaxResult));
    return false;
  }

  /**
   * 对跨域提供支持
   */
  @Override
  protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
    HttpServletRequest httpServletRequest = (HttpServletRequest) request;
    HttpServletResponse httpServletResponse = (HttpServletResponse) response;
    httpServletResponse
        .setHeader("Access-control-Allow-Origin", httpServletRequest.getHeader("Origin"));
    httpServletResponse.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE");
    httpServletResponse.setHeader("Access-Control-Allow-Headers",
        httpServletRequest.getHeader("Access-Control-Request-Headers"));
    // 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态
    if (httpServletRequest.getMethod().equals(RequestMethod.OPTIONS.name())) {
      httpServletResponse.setStatus(HttpStatus.OK.value());
      return false;
    }
    return super.preHandle(request, response);
  }

  /**
   * 非法请求路由到/401
   */
  private void response401(ServletRequest req, ServletResponse resp) {
    try {
      HttpServletResponse response = (HttpServletResponse) resp;
      AjaxResult ajaxResult = AjaxResult.failed(ResultCodeEnum.UNAUTH.code(),
          ResultCodeEnum.UNAUTH.message());
      response.getWriter().write(Json.toJsonString(ajaxResult));
    } catch (Exception e) {
      logger.error("response401异常", e);
    }
  }

  /**
   * 解析cookie 获取token
   */
  private JWTToken analyzeJWTToken(ServletRequest request) {
    HttpServletRequest httpServletRequest = WebUtils.toHttp(request);
    JWTToken jwtToken = new JWTToken();
    jwtToken.setUrl(httpServletRequest.getRequestURI());
    Cookie[] cookies = httpServletRequest.getCookies();
    if (cookies == null || cookies.length <= 0) {
      throw new AuthenticationException("cookie为null");
    }
    for (Cookie cookie : cookies) {
      if (OperateShiro.TOKENNAME.equals(cookie.getName())) {
        jwtToken.setToken(cookie.getValue());
        return jwtToken;
      }
    }
    throw new AuthenticationException("未查询到cookieName=" + OperateShiro.TOKENNAME);
  }
}

4、JWT token封装

import org.apache.shiro.authc.AuthenticationToken;

public class JWTToken implements AuthenticationToken {

  /**
   * token
   */
  private String token;
  /**
   * 请求url
   */
  private String url;

  public JWTToken() {
  }

  public JWTToken(String token) {
    this.token = token;
  }

  public JWTToken(String token, String url) {
    this.token = token;
    this.url = url;
  }

  public String getToken() {
    return token;
  }

  public void setToken(String token) {
    this.token = token;
  }

  public String getUrl() {
    return url;
  }

  public void setUrl(String url) {
    this.url = url;
  }

  @Override
  public Object getPrincipal() {
    return this;
  }

  @Override
  public Object getCredentials() {
    return this;
  }
}

5、登录接口 将token 写入cookie

@Override
  public void login(String username, String password, HttpServletResponse response) {
    if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
      throw new RiskBusinessException("用户或者密码不能为空");
    }
    Example example = new Example(OperateUserEntity.class);
    Criteria criteria = example.createCriteria();
    criteria.andEqualTo("username", username);
    password = CommonUtil.md5(password);
    criteria.andEqualTo("password", password);
    OperateUserEntity operateUserEntity = userService.selectOneByExample(example);
    Preconditions.checkArgument(operateUserEntity != null, "账号或者密码错误");
    Preconditions.checkArgument(operateUserEntity.getStatus() != 0, "用户未启用");
    String token = JWTUtil.sign("userId", String.valueOf(operateUserEntity.getId()), password);
    // 认证成功写入cookie
    Cookie cookie = new Cookie(OperateShiro.TOKENNAME, token);
    cookie.setDomain(GlobalOpetateProperties.domain);
    cookie.setPath("/");
    // 设置有效期12小时(单位:秒)
    cookie.setMaxAge(12 * 60 * 60);
    response.addCookie(cookie);
  }

5、整体的流程为

简易使用shrio 实现 登录验证_第1张图片

 分享:https://www.jianshu.com/p/0b1131be7ace

你可能感兴趣的:(java,spring)