spring boot 、mybatis-plus、shiro整合入门教程(三)——shiro使用

接续上一篇,spring boot 、mybatis-plus、shiro整合入门教程(二)——mybatis-plus常用操作

这篇主要介绍下shiro在项目中是如何使用的,
前期的主要工作是数据库表的设计,上篇文章已经给出表的设计,详细的数据库初始化sql,可以参考代码库的sql,
简单说下登录原理,用户每次登录的时候生成token,同时根据用户id更新数据库中的token有效时间和token值,用户下次登录的时候会去校验token的有效时间,不在有效期内会返回登录页重新登录刷新。
登录的代码和shiro整合的相关代码:

/**
     * 登录
     * @param username
     * @param password
     * @param captcha
     * @param randomStr
     * @return
     */
    @RequestMapping (value="/login", method = RequestMethod.POST)
    public Map<String, Object> login(String username, String password,String captcha, String randomStr) {
        String value = (String)redisTemplate.opsForValue().get(Constants.NUMBER_CODE_KEY + randomStr);
        if (StringUtils.isBlank(value)) {
            return WebResult.error("验证码过期");
        }
        if (!captcha.equals(value)) {
            return WebResult.error("验证码不正确");
        }
        SysUser sysUser = sysUserService.getOne(username);
        if(sysUser == null || !sysUser.getPassword().equals(new Sha256Hash(password, sysUser.getSalt()).toHex())) {
            return WebResult.error("账号或密码不正确");
        }
        if(sysUser.getStatus() == 0){
            return WebResult.error("账号已被锁定,请联系管理员");
        }
        //生成token 保存到数据库  可优化为存储到redis和数据库
        return sysUserTokenService.createToken(sysUser.getUserId());
    }
/**
     * 生成token
     * @param userId
     * @return
     */


    public WebResult createToken(long userId) {
        String token = TokenGenerator.generateValue();
        //当前时间
        LocalDateTime now = LocalDateTime.now();
        //过期时间
        LocalDateTime expireTime = now.plusHours(Constants.EXPIRE);
        //判断是否生成token
        SysUserToken sysUserToken = sysUserTokenMapper.selectById(userId);
        if(sysUserToken == null){
            sysUserToken = new SysUserToken();
            sysUserToken.setUserId(userId);
            sysUserToken.setToken(token);
            sysUserToken.setUpdateTime(now);
            sysUserToken.setExpireTime(expireTime);
            baseMapper.insert(sysUserToken);
        }else{
            sysUserToken.setToken(token);
            sysUserToken.setUpdateTime(now);
            sysUserToken.setExpireTime(expireTime);
            sysUserTokenMapper.updateById(sysUserToken);
        }
        return new WebResult().put("token", token).put("expire", expireTime);
    }

项目中使用shiro需要继承AuthenticatingFilter和AuthorizingRealm,代码如下:

/**
 * @author liuyi
 * @date 2019/5/15
 *
 * oauth 过滤器
 */

@Slf4j
public class OAuthFilter extends AuthenticatingFilter {

    @Override
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
        String token = getRequestToken((HttpServletRequest) request);

        if(StringUtils.isBlank(token)){
            return null;
        }

        return new OAuthToken(token);
    }

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        if (request instanceof HttpServletRequest) {
            if (((HttpServletRequest) request).getMethod().toUpperCase().equals("OPTIONS")) {
                return true;
            }
        }
        return super.isAccessAllowed(request, response, mappedValue);
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        //获取请求token,如果token不存在,直接返回401
        String token = getRequestToken((HttpServletRequest) request);
        if(StringUtils.isBlank(token)){
            HttpServletResponse httpResponse = (HttpServletResponse) response;
            String json = JSON.toJSONString(WebResult.error(HttpStatus.SC_UNAUTHORIZED, "invalid token"));
            httpResponse.getWriter().print(json);

            return false;
        }
        return executeLogin(request, response);
    }

    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException ep, ServletRequest request, ServletResponse response) {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        httpResponse.setContentType("application/json;charset=utf-8");
        try {
            //处理登录失败的异常
            Throwable throwable = ep.getCause() == null ? ep : ep.getCause();
            WebResult result = WebResult.error(HttpStatus.SC_UNAUTHORIZED, throwable.getMessage());
            String json = JSON.toJSONString(result);
            httpResponse.getWriter().print(json);
        } catch (IOException e) {
            log.error("OAuthFilter 异常", e);
        }
        return false;
    }

    /**
     * 获取请求的token
     * @param httpRequest
     * @return
     */
    private String getRequestToken(HttpServletRequest httpRequest){
        //从header中获取token
        String token = httpRequest.getHeader("token");
        //如果header中不存在token,则从参数中获取token
        if(StringUtils.isBlank(token)){
            token = httpRequest.getParameter("token");
        }
        return token;
    }
}

@Component
public class OAuthRealm extends AuthorizingRealm {

    @Resource
    private ShiroServiceImpl shiroService;

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

    /**
     * 授权(验证权限时调用)
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SysUser user = (SysUser)principals.getPrimaryPrincipal();
        Long userId = user.getUserId();
        //用户权限列表
        Set<String> permsSet = shiroService.getUserPermissions(userId);
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.setStringPermissions(permsSet);
        return info;
    }

    /**
     * 认证
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String accessToken = (String) token.getPrincipal();
        //根据accessToken,查询用户信息
        SysUserToken tokenEntity = shiroService.queryByToken(accessToken);
        //token失效
        if(tokenEntity == null || tokenEntity.getExpireTime().toInstant(ZoneOffset.of("+8")).toEpochMilli() < System.currentTimeMillis()){
            throw new IncorrectCredentialsException("token失效,请重新登录");
        }
        //查询用户信息
        SysUser user = shiroService.queryUser(tokenEntity.getUserId());
        //账号锁定
        if(user.getStatus() == 0){
            throw new LockedAccountException("账号已被锁定,请联系管理员");
        }
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, accessToken, getName());
        return info;
    }
}

public class OAuthToken implements AuthenticationToken {

    private String token;

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

    @Override
    public String getPrincipal() {
        return token;
    }

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

/**
 * @author liuyi
 * @date 2019/5/15
 */

@Configuration
public class ShiroConfig {

    /**
     * 权限过滤器
     * @param securityManager
     * @return
     */
    @Bean(name="shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager);
        shiroFilter.setLoginUrl("/sys/unauthorized");
        shiroFilter.setUnauthorizedUrl("/sys/unauthorized");
        //oauth过滤
        Map<String, Filter> filters = new HashMap<>();
        filters.put("oauth", new OAuthFilter());
        shiroFilter.setFilters(filters);
        Map<String, String> filterMap = new LinkedHashMap<>();
        filterMap.put("/actuator/**", "anon");
        //APP 模块开放 后面通过拦截器管理
        filterMap.put("/app/**", "anon");
        //用户密码登录
        filterMap.put("/sys/login", "anon");
        //未认证
        filterMap.put("/sys/unauthorized", "anon");
        //验证码
        filterMap.put("/sys/captcha/**", "anon");
        filterMap.put("/v2/**", "anon");
        filterMap.put("/", "anon");
        filterMap.put("/**", "oauth");
        shiroFilter.setFilterChainDefinitionMap(filterMap);
        return shiroFilter;
    }

    /**
     * 配置安全事务管理器
     * @param authRealm
     * @param sessionManager
     * @return
     */
    @Bean("securityManager")
    public SecurityManager securityManager(OAuthRealm authRealm, SessionManager sessionManager) {
        DefaultWebSecurityManager manager=new DefaultWebSecurityManager();
        manager.setRealm(authRealm);
        manager.setSessionManager(sessionManager);
        return manager;
    }

    /**
     * session管理
     * @return
     */
    @Bean("sessionManager")
    public SessionManager sessionManager(){
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        sessionManager.setSessionValidationSchedulerEnabled(true);
        sessionManager.setSessionIdCookieEnabled(true);
        return sessionManager;
    }



    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
        return new LifecycleBeanPostProcessor();
    }
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator creator=new DefaultAdvisorAutoProxyCreator();
        creator.setProxyTargetClass(true);
        return creator;
    }
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor=new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager);
        return advisor;
    }
}

/**
 * @author liuyi
 * @date 2019/5/15
 */

@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean shiroFilterRegistration() {
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(new DelegatingFilterProxy("shiroFilter"));
        registrationBean.addInitParameter("targetFilterLifecycle", "true");
        registrationBean.setEnabled(true);
        registrationBean.setOrder(Integer.MAX_VALUE - 1);
        registrationBean.addUrlPatterns("/*");
        return registrationBean;
    }

    @Bean
    public FilterRegistrationBean xssFilterRegistration() {
        FilterRegistrationBean registration = new FilterRegistrationBean();
        registration.setDispatcherTypes(DispatcherType.REQUEST);
        registration.setFilter(new XssFilter());
        registration.addUrlPatterns("/*");
        registration.setName("xssFilter");
        registration.setOrder(Integer.MAX_VALUE);
        return registration;
    }
}

代码地址
下一篇

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值