Spring MVC 中急速集成 Shiro 实践

简介:

 相信有很多的程序员,不愿意进行用户管理这块代码实现。

   原因之一,不同的JavaEE 系统,用户管理都会有个性化的实现,逻辑很繁琐。

   而且是系统门面,以后背锅的几率非常大,可谓是低收益高风险。

   最近在系统中集成了 Shiro,感觉这个小家伙还是相当灵活的。

   完善的用户认证和授权,干净的API,让人如沐春分。

   Apache Shiro 作为一个强大而灵活的开源安全框架,它干净利落地处理身份认证,授权,企业会话管理和加密。

   安全有时候是很复杂的,甚至是痛苦的,但它没有必要这样。框架应该尽可能掩盖复杂的地方,露出一个干净而直观的 API。

   Apache Shiro 的首要目标是易于使用和理解。

   以下是你可以用 Apache Shiro 所做的事情:

   a.验证用户来核实他们的身份

   b.对用户执行访问控制,如:

   c.判断用户是否被分配了一个确定的安全角色。

   d.判断用户是否被允许做某事。

   e.在任何环境下使用 Session API,即使没有 Web 或 EJB 容器。

   f.在身份验证,访问控制期间或在会话的生命周期,对事件作出反应。

   g.聚集一个或多个用户安全数据的数据源,并作为一个单一的复合用户“视图”。

   h.启用单点登录(SSO)功能。

   i.并发登录管理(一个账号多人登录作踢人操作)。

   j.为没有关联到登录的用户启用"Remember Me"服务。

   …

   以及更多——全部集成到紧密结合的易于使用的 API 中。

   目前主流安全框架有 SpringSecurity 和 Shiro,相比于 SpringSecurity,Shiro 轻量化,简单容易上手。

   SpringSecurity 太笨重了,难以上手,且只能在 Spring 里用,所以极力推荐Shiro。

   本文重点描述集成过程,能让你迅速的将 Shiro 集成到 JavaEE 项目中,毕竟项目都挺紧张的。

1.前戏

    Shiro 核心jar:

复制代码
       <!--权限控制 shiro-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>${shiro.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-ehcache</artifactId>
            <version>${shiro.version}</version>
        </dependency>
复制代码

    JavaEE 应用开始的地方,web.xml 配置:

复制代码
         <filter> 
           <filter-name>shiroFilter</filter-name> 
           <filter-class> 
              org.springframework.web.filter.DelegatingFilterProxy 
           </filter-class> 
         </filter> 
         <filter-mapping> 
           <filter-name>shiroFilter</filter-name> 
           <url-pattern>/*</url-pattern> 
         </filter-mapping>
复制代码

   Spring-Shiro-context 配置应用启动的使用,会帮助你做很多事情:

复制代码
   <!--Shiro 关键过滤器配置-->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager"/>
        <property name="loginUrl" value="/sys/login"/> <!--请求 Url 为 get方式-->
        <property name="successUrl" value="/sys/index"/>
        <property name="filters">
            <map>
                <entry key="authc" value-ref="formAuthenticationFilter"/>
            </map>
        </property>
        <property name="filterChainDefinitions" ref="shiroFilterChainDefinitions"/>
    </bean>

    <!-- Shiro 安全管理器 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="systemAuthorizingRealm"/>
        <property name="cacheManager" ref="shiroCacheManager"/>
    </bean>

    <!--自定义系统认证域-->
    <bean id="systemAuthorizingRealm" class="com.rambo.spm.core.shiro.SysAuthorizingRealm"/>

    <!--shiro ehcache缓存-->
    <bean id="shiroCacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <property name="cacheManager" ref="cacheManagerFactory"/>
    </bean>

    <!--扩展表单认证过滤器-->
    <bean id="formAuthenticationFilter" class="com.rambo.spm.core.shiro.FormAuthenticationFilter"/>

    <!--权限过滤链定义 -->
    <bean name="shiroFilterChainDefinitions" class="java.lang.String">
        <constructor-arg>
            <value>
                /static/** = anon
                /captcha-image = anon
                /sys/login = authc
                /sys/logout = logout
                /** =user
            </value>
        </constructor-arg>
    </bean>

    <!--借助 SpringAOP 扫描那些使用 Shiro 注解的类-->
    <aop:config proxy-target-class="true"/>

    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean>

    <!--用于在实现了Initializable/Destroyable接口的 Shiro bean 初始化时回调-->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
复制代码

2.个性化

   请求发起的地方一般是前端,不管你是 .jsp/.php/.net.......方式都是类似

复制代码
<html>
<body>
<h1>login page</h1>
<form id="" action="service/dologin" method="post">
    <label>账号:</label><input name="userName" maxLength="40"/>
    <input title="是否是管理员" type="checkbox" name="isAdmin"><label>是否为管理员</label><br>
    <label>密码:</label><input title="密码" type="password" name="password" /><br>
    <input type="submit" value="登录"/>
</form>
<%--用于输入后台返回的验证错误信息 --%>
<P><c:out value="${message }"/></P>
</body>
</html>
复制代码

   自定义项目验证域(验证域可以有多个,已可以有多种方式)。

复制代码
public class SysAuthorizingRealm extends AuthorizingRealm {
    private Log log = LogFactory.get();

    @Autowired
    private SysUserService sysUserService;

    @Autowired
    private SysRoleService sysRoleService;

    @Autowired
    private SysMenuService sysMenuService;


    /**
     * 获取当前用户的认证信息
     *
     * @param authcToken 携带用户认证所需的信息
     * @return 认证结果信息
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
        CaptchaUsernamePasswordToken spmToken = (CaptchaUsernamePasswordToken) authcToken;
        log.info("token:{}", ReflectionToStringBuilder.toString(spmToken));

        SysUser sysUser = sysUserService.getSysUserByLoginName(spmToken.getUsername());
        if (sysUser == null) {
            return null;
        }
        byte[] salt = Hex.decode(sysUser.getPasswd().substring(0, 16));
        return new SimpleAuthenticationInfo(new SpmPrincipal(sysUser), sysUser.getPasswd().substring(16), ByteSource.Util.bytes(salt), getName());
    }

    /**
     * 获取当前用户授权信息
     *
     * @param principals 该用户身份集合
     * @return 当前用户授权信息
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SpmPrincipal spmPrincipal = (SpmPrincipal) super.getAvailablePrincipal(principals);
        log.info("授权当前:{}", ReflectionToStringBuilder.toString(spmPrincipal));

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

        List<SysRole> sysRoleList = sysUserService.listSysRoleByUserId(spmPrincipal.getId());
        for (SysRole sysRole : sysRoleList) {
            info.addRole(sysRole.getRoleType());

            List<SysMenu> sysMenuList = sysRoleService.listSysMenuByRoleId(sysRole.getUuid());
            for (SysMenu sysMenu : sysMenuList) {
                info.addStringPermission(sysMenu.getPermisson());
            }
        }
        info.addStringPermission("user");
        return info;
    }

    /**
     * 设定密码校验的Hash算法与迭代次数
     */
    @PostConstruct
    public void initCredentialsMatcher() {
        HashedCredentialsMatcher matcher = new HashedCredentialsMatcher("SHA-1");
        matcher.setHashIterations(1024);
        setCredentialsMatcher(matcher);
    }
}
复制代码

    请求的后台服务,在这里你可以在进行一点业务逻辑

复制代码
   /**
     * shiro 登录请求控制,真正的请求由 shiroFilter --> formAuthenticationFilter 进行处理
     * 登录成功: 跳转配置的 succssUrl,不触发该方法;
     * 登录失败: 触发该方法,可以从扩展的 formAuthenticationFilter 中获取具体的错误信息;
     */
    @PostMapping("/sys/login")
    public Object postSysLogin(HttpServletRequest httpRequest, ModelAndView modelAndView) {
        String exceptionName = (String) httpRequest.getAttribute(FormAuthenticationFilter.DEFAULT_ERROR_KEY_ATTRIBUTE_NAME);

        String errorMsg = null;
        if (IncorrectCaptchaException.class.getName().equals(exceptionName)) {
            errorMsg = "验证码错误!";
        } else if (UnknownAccountException.class.getName().equals(exceptionName)) {
            errorMsg = "用户不存在!";
        } else if (IncorrectCredentialsException.class.getName().equals(exceptionName)) {
            errorMsg = "用户或密码错误!";
        } else if (exceptionName != null && StrUtil.startWith(exceptionName, "msg:")) {
            errorMsg = StrUtil.removeAll(exceptionName, "msg:");
        }
        return setModelAndView(modelAndView, "sys/sysLogin", errorMsg);
    }
复制代码

     整个集成工作就结束了,是不是简单的不要不要的。

     等下次项目经理指派你做用户管理的时候,只需要花半天的时间做设计。

     借助Shiro 半天时间实现代码。

     然后将剩下的时间,做做自己喜欢的其他研究工作。

本文转自Orson博客园博客,原文链接:http://www.cnblogs.com/java-class/p/5475373.html,如需转载请自行联系原作者

相关文章
|
3天前
|
人工智能 自然语言处理 Java
Spring 集成 DeepSeek 的 3大方法(史上最全)
DeepSeek 的 API 接口和 OpenAI 是兼容的。我们可以自定义 http client,按照 OpenAI 的rest 接口格式,去访问 DeepSeek。自定义 Client 集成DeepSeek ,可以通过以下步骤实现。步骤 1:准备工作访问 DeepSeek 的开发者平台,注册并获取 API 密钥。DeepSeek 提供了与 OpenAI 兼容的 API 端点(例如),确保你已获取正确的 API 地址。
Spring 集成 DeepSeek 的 3大方法(史上最全)
|
20天前
|
SQL Java 数据库连接
对Spring、SpringMVC、MyBatis框架的介绍与解释
Spring 框架提供了全面的基础设施支持,Spring MVC 专注于 Web 层的开发,而 MyBatis 则是一个高效的持久层框架。这三个框架结合使用,可以显著提升 Java 企业级应用的开发效率和质量。通过理解它们的核心特性和使用方法,开发者可以更好地构建和维护复杂的应用程序。
112 29
|
1月前
|
监控 Java Nacos
使用Spring Boot集成Nacos
通过上述步骤,Spring Boot应用可以成功集成Nacos,利用Nacos的服务发现和配置管理功能来提升微服务架构的灵活性和可维护性。通过这种集成,开发者可以更高效地管理和部署微服务。
208 17
|
1月前
|
搜索推荐 NoSQL Java
微服务架构设计与实践:用Spring Cloud实现抖音的推荐系统
本文基于Spring Cloud实现了一个简化的抖音推荐系统,涵盖用户行为管理、视频资源管理、个性化推荐和实时数据处理四大核心功能。通过Eureka进行服务注册与发现,使用Feign实现服务间调用,并借助Redis缓存用户画像,Kafka传递用户行为数据。文章详细介绍了项目搭建、服务创建及配置过程,包括用户服务、视频服务、推荐服务和数据处理服务的开发步骤。最后,通过业务测试验证了系统的功能,并引入Resilience4j实现服务降级,确保系统在部分服务故障时仍能正常运行。此示例旨在帮助读者理解微服务架构的设计思路与实践方法。
105 17
|
1月前
|
缓存 安全 Java
Spring Boot 3 集成 Spring Security + JWT
本文详细介绍了如何使用Spring Boot 3和Spring Security集成JWT,实现前后端分离的安全认证概述了从入门到引入数据库,再到使用JWT的完整流程。列举了项目中用到的关键依赖,如MyBatis-Plus、Hutool等。简要提及了系统配置表、部门表、字典表等表结构。使用Hutool-jwt工具类进行JWT校验。配置忽略路径、禁用CSRF、添加JWT校验过滤器等。实现登录接口,返回token等信息。
371 12
|
1月前
|
人工智能 安全 Dubbo
Spring AI 智能体通过 MCP 集成本地文件数据
MCP 作为一款开放协议,直接规范了应用程序如何向 LLM 提供上下文。MCP 就像是面向 AI 应用程序的 USB-C 端口,正如 USB-C 提供了一种将设备连接到各种外围设备和配件的标准化方式一样,MCP 提供了一个将 AI 模型连接到不同数据源和工具的标准化方法。
|
1月前
|
存储 安全 Java
Spring Boot 3 集成Spring AOP实现系统日志记录
本文介绍了如何在Spring Boot 3中集成Spring AOP实现系统日志记录功能。通过定义`SysLog`注解和配置相应的AOP切面,可以在方法执行前后自动记录日志信息,包括操作的开始时间、结束时间、请求参数、返回结果、异常信息等,并将这些信息保存到数据库中。此外,还使用了`ThreadLocal`变量来存储每个线程独立的日志数据,确保线程安全。文中还展示了项目实战中的部分代码片段,以及基于Spring Boot 3 + Vue 3构建的快速开发框架的简介与内置功能列表。此框架结合了当前主流技术栈,提供了用户管理、权限控制、接口文档自动生成等多项实用特性。
85 8
|
1月前
|
人工智能 自然语言处理 Java
Spring Cloud Alibaba AI 入门与实践
本文将介绍 Spring Cloud Alibaba AI 的基本概念、主要特性和功能,并演示如何完成一个在线聊天和在线画图的 AI 应用。
361 7
|
4月前
|
Java Maven Docker
gitlab-ci 集成 k3s 部署spring boot 应用
gitlab-ci 集成 k3s 部署spring boot 应用
|
3月前
|
消息中间件 监控 Java
您是否已集成 Spring Boot 与 ActiveMQ?
您是否已集成 Spring Boot 与 ActiveMQ?
79 0

热门文章

最新文章