SSM+JWT+Shiro

用户登录后生成token返回前端。前端每次请求时都在Header中添加token,后端验证token

一、禁用shiro的session


public class StatelessDefaultSubjectFactory extends DefaultWebSubjectFactory {
    @Override
    public Subject createSubject(SubjectContext context) {
        context.setSessionCreationEnabled(false);//关闭session创建
        return super.createSubject(context);
    }
}

二、重写filter过滤器

@Slf4j
public class AuthenFilter extends BasicHttpAuthenticationFilter {

    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        boolean flag = false;
        HttpServletRequest servletRequest = WebUtils.toHttp(request);
        String token = servletRequest.getHeader("token");
        if (token != null) {
            Claims claims = null;
            try {
                claims = JwtUtil.parsetJwt(token);
                this.getSubject(request, response).login(new JwtToken(token));
            } catch (Exception throwable) {
                String msg = null;
                if (throwable instanceof SignatureException) {
                    // 该异常为JWT的AccessToken认证失败(Token或者密钥不正确)
                    msg = "Token或者密钥不正确(" + throwable.getMessage() + ")";
                } else if (throwable instanceof ExpiredJwtException) {
                    msg = "Token已过期(" + throwable.getMessage() + ")";
                } else {
                    msg = throwable.getMessage();
                }
                // Token认证失败直接返回Response信息
                this.response401(response, msg);
            }
            if (claims != null && claims.getExpiration().getTime() > System.currentTimeMillis()) flag = true;
        }

        return flag;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws IOException {
        HttpServletResponse servletResponse = WebUtils.toHttp(response);

        servletResponse.getWriter().write(JsonUtil.returnJson(4001, "未认证", null));
        return false;
    }

    /**
     * 无需转发,直接返回Response信息
     */
    private void response401(ServletResponse response, String msg) {
        HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
        httpServletResponse.setStatus(HttpStatus.OK.value());
        httpServletResponse.setCharacterEncoding("UTF-8");
        httpServletResponse.setContentType("application/json; charset=utf-8");
        try (PrintWriter out = httpServletResponse.getWriter()) {
            String data = JSON.toJSONString(new ResponseBean(HttpStatus.UNAUTHORIZED.value(), "无权访问(Unauthorized):" + msg, null));
            out.append(data);
        } catch (IOException e) {
            throw new CustomException("直接返回Response信息出现IOException异常:" + e.getMessage());
        }
    }

}

当isAccessAllowed返回为true时,直接允许访问,返回false时,shiro根据onAccessDenied的返回值决定是否允许访问

三、重写Realm类,防止用户登录后修改地址访问无权限接口

@Component
public class MyShiroRealm extends AuthorizingRealm {
    @Autowired
    @Lazy
    private IBdOrganizeEmployeeService iBdOrganizeEmployeeService;
    @Autowired
    private IBdSecuritySourcesService iBdSecuritySourcesService;


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

    private int i = 0;
    /**
     * 重写shiro权限验证,判断已分配给用户的菜单编码(现在放在ul中)和要请求的 @RequiresPermissions能否对应上,对应不上就是无权限
     * @param principalCollection
     * @return org.apache.shiro.authz.AuthorizationInfo
     * @author yueshibing
     * @data 2023/1/30 16:51
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        LoggerFactory.getLogger(MyShiroRealm.class).debug(String.valueOf(i));
        Object principal = principalCollection.getPrimaryPrincipal();
        //String userName = JwtUtil.parsetJwt(principal.toString()).get("userName").toString();
        String userId = JwtUtil.parsetJwt(principal.toString()).get("userId").toString();
        //获取菜单url
        List<BdSecuritySources> bdSecuritySourcesList = iBdSecuritySourcesService.getBdSecuritySourcesByUserID(userId);
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        bdSecuritySourcesList.forEach(p -> {
            info.addStringPermission(p.getUl());
        });
        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        JwtToken token = (JwtToken) authenticationToken;
        Object credentials = token.getCredentials();
        Claims claims = JwtUtil.parsetJwt(credentials.toString());
        Object userName = claims.get("userName");
        QueryWrapper<BdOrganizeEmployee> wrapper = new QueryWrapper<>();
        wrapper.select("USER_NAME");
        wrapper.eq("USER_NAME", userName);
        BdOrganizeEmployee adminuser = iBdOrganizeEmployeeService.getOne(wrapper);
        Object password = "";
        if (adminuser != null) password = token.getCredentials();
        return new SimpleAuthenticationInfo(
                token.getPrincipal(),
                password,
//                ByteSource.Util.bytes(username),
                this.getName()
        );
    }
}

判断用户菜单权限,和注解@RequiresPermissions匹配,匹配成功才可以访问,该注解用于控制器或者具体的某个方法上

四、shiro配置类

@Configuration
public class ShiroConfig {

    @Bean
    @Order(Integer.MAX_VALUE)
    public ShiroFilterFactoryBean shiroFilterFactoryBean() {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();

        //设置securityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager());
        //配置自定义过滤器
        Map<String, Filter> filterMap = new HashMap<String, Filter>();
        filterMap.put("jwtAuthen", new AuthenFilter());
        shiroFilterFactoryBean.setFilters(filterMap);
        //配置匹配规则
        LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/login/login", "anon");
        filterChainDefinitionMap.put("/login/getLoginCode", "anon");
//        filterChainDefinitionMap.put("/login/loginOut", "anon");
        filterChainDefinitionMap.put("/**", "jwtAuthen");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return shiroFilterFactoryBean;
    }

    @Bean
    public SessionsSecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        //配置自定义realm
        securityManager.setRealm(realm());
        //关闭session
        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
        DefaultSessionStorageEvaluator sessionStorageEvaluator = new DefaultSessionStorageEvaluator();
        sessionStorageEvaluator.setSessionStorageEnabled(false);
        subjectDAO.setSessionStorageEvaluator(sessionStorageEvaluator);
        securityManager.setSubjectDAO(subjectDAO);

        //配置无session工厂类
        securityManager.setSubjectFactory(subjectFactory());

        return securityManager;
    }

    @Bean
    public Realm realm() {
        MyShiroRealm realm = new MyShiroRealm();
        return realm;
    }

    //配置无状态session工厂类
    public StatelessDefaultSubjectFactory subjectFactory() {
        return new StatelessDefaultSubjectFactory();
    }

    /**
     *  开启Shiro的注解(如@RequiresRoles,@RequiresPermissions)
     * @return
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

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

    /**
     * 开启aop注解支持
     * @param securityManager
     * @return
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SessionsSecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
SSM(Spring+SpringMVC+MyBatis)是一种Java Web开发框架,被广泛使用于企业级应用的开发中。Spring提供了依赖注入和面向切面编程的特性,Spring MVC是一种轻量级的Web框架,而MyBatis则是一种持久层框架,可以方便地操作数据库。 Layui是一个简单易用、高效轻量级的前端框架,可以帮助前端开发者快速搭建Web界面。Layui提供了丰富的UI组件和灵活的样式定制功能,能够帮助开发者实现页面的快速展示和交互。 Shiro是一个强大且易于使用的Java安全框架,提供了认证、授权、会话管理和加密等安全控制功能。Shiro能够帮助开发者轻松地实现用户身份验证和访问控制,提高系统的安全性。 综合上述三者,使用SSM框架可以简化Java Web的开发流程,通过Spring提供的依赖注入和面向切面编程,我们能够更好地管理和维护项目的各个组件。使用SpringMVC可以方便地开发出符合MVC设计模式的Web应用程序,而MyBatis则提供了对数据库的便捷访问,可以避免直接编写繁琐的SQL语句。 在前端方面,Layui的轻量级特性和丰富的UI组件能够加快前端开发的速度,使页面的搭建变得简单高效。 而使用Shiro可以保护我们的应用程序,通过身份验证和访问控制,可以确定用户的身份并限制其权限,提高系统的安全性。 总的来说,SSM、Layui和Shiro三者结合使用可以提高我们开发的效率和安全性,同时也能够提供用户友好的界面体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

木木的成长之路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值