SSM框架整合Shiro

一、环境准备

1、创建Maven项目,在pom.xml中添加依赖

xml 复制代码
<dependencies>
  <!-- Shiro -->
  <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
    <version>1.7.1</version>
  </dependency>
  <dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
    <version>1.7.1</version>
  </dependency>
  
  <!-- Spring -->
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.14</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.14</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>5.3.14</version>
  </dependency>
  
  <!-- MyBatis -->
  <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.7</version>
  </dependency>
  <dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>2.0.7</version>
  </dependency>
  
  <!-- MySQL Connector -->
  <dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.27</version>
  </dependency>
</dependencies>

2、配置数据源和MyBatis

在src/main/resources目录下创建一个名为applicationContext.xml的Spring配置文件,并配置数据源和MyBatis。

xml 复制代码
<!-- 数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
  <property name="url" value="jdbc:mysql://localhost:3306/my_database?useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=Asia/Shanghai"/>
  <property name="username" value="root"/>
  <property name="password" value="root"/>
</bean>

<!-- MyBatis -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource"/>
  <property name="typeAliasesPackage" value="com.example.model"/>
  <property name="mapperLocations" value="classpath*:mapper/*.xml"/>
</bean>

<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
  <constructor-arg ref="sqlSessionFactory"/>
</bean>

3、配置Shiro

在src/main/resources目录下创建一个名为shiro.ini的Shiro配置文件,并配置Shiro。

xml 复制代码
[main]
# Realm配置
myRealm = com.example.shiro.MyRealm
securityManager.realm = $myRealm

# 编码器配置
hashedCredentialsMatcher = org.apache.shiro.authc.credential.HashedCredentialsMatcher
hashedCredentialsMatcher.hashAlgorithmName = SHA-256
hashedCredentialsMatcher.hashIterations = 1
myRealm.credentialsMatcher = $hashedCredentialsMatcher

# 缓存配置
cacheManager = org.apache.shiro.cache.ehcache.EhCacheManager
securityManager.cacheManager = $cacheManager
ehcacheManager.cacheManagerConfigFile = classpath:ehcache.xml
cacheManager.cacheManager = $ehcacheManager

# 过滤器配置
authc = org.apache.shiro.web.filter.authc.FormAuthenticationFilter
authc.loginUrl = /login.jsp
authc.successUrl = /index.jsp
authc.usernameParam = username
authc.passwordParam = password
authc.rememberMeParam = rememberMe

perms = org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter

[urls]
# anon表示不需要认证
# authc表示需要认证
# perms表示需要权限
/login.jsp = anon
/logout = anon
/index.jsp = authc,perms[user:view]
/user/list = authc,perms[user:view]
/user/add = authc,perms[user:add]
/user/edit = authc,perms[user:edit]
/user/delete = authc,perms[user:delete]

3、编写MyRealm

com.example.shiro包下创建一个名为MyRealm的类,并继承org.apache.shiro.realm.AuthorizingRealm

java 复制代码
public class MyRealm extends AuthorizingRealm {

  @Autowired
  private UserService userService;

  /**
   * 授权
   */
  @Override
  protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
    User user = (User) principalCollection.getPrimaryPrincipal();
    List<String> permissions = userService.getPermissionsByUserId(user.getId());
    authorizationInfo.addStringPermissions(permissions);
    return authorizationInfo;
  }

  /**
   * 认证
   */
  @Override
  protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
    User user = userService.getUserByUsername(token.getUsername());
    if (user == null) {
      throw new UnknownAccountException();
    }
    SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, user.getPassword(), getName());
    return authenticationInfo;
  }
}

4、编写UserService

在com.example.service包下创建一个名为UserService的接口,并定义获取用户信息和权限的方法。

java 复制代码
public interface UserService {
  User getUserByUsername(String username);
  List<String> getPermissionsByUserId(Long userId);
}

在com.example.service.impl包下创建一个名为UserServiceImpl的类,并实现UserService接口。

java 复制代码
@Service
public class UserServiceImpl implements UserService {

  @Autowired
  private UserMapper userMapper;

  @Autowired
  private PermissionMapper permissionMapper;

  @Override
  public User getUserByUsername(String username) {
    return userMapper.getUserByUsername(username);
  }

  @Override
  public List<String> getPermissionsByUserId(Long userId) {
    return permissionMapper.getPermissionsByUserId(userId);
  }
}

5、编写Controller

在com.example.controller包下创建一个名为UserController的类,并编写处理请求的方法。

java 复制代码
@Controller
@RequestMapping("/user")
public class UserController {

  @Autowired
private UserService userService;

@RequestMapping("/list")
@RequiresPermissions("user:view")
public String list(Model model) {
List<User> userList = userService.getUserList();
model.addAttribute("userList", userList);
return "user/list";
}

@RequestMapping("/add")
@RequiresPermissions("user:add")
public String add() {
return "user/add";
}

@RequestMapping("/edit/{id}")
@RequiresPermissions("user:edit")
public String edit(@PathVariable("id") Long id, Model model) {
User user = userService.getUserById(id);
model.addAttribute("user", user);
return "user/edit";
}

@RequestMapping("/update")
@RequiresPermissions("user:edit")
public String update(User user) {
userService.updateUser(user);
return "redirect:/user/list";
}

@RequestMapping("/delete/{id}")
@RequiresPermissions("user:delete")
public String delete(@PathVariable("id") Long id) {
userService.deleteUserById(id);
return "redirect:/user/list";
}
}

6、编写登录页面

在src/main/webapp下创建一个名为login.jsp的登录页面。

html 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
  <title>登录</title>
</head>
<body>
  <form action="/login" method="post">
    <input type="text" name="username" placeholder="用户名"><br>
    <input type="password" name="password" placeholder="密码"><br>
    <input type="checkbox" name="rememberMe">记住我<br>
    <button type="submit">登录</button>
  </form>
</body>
</html>

7、编写权限控制页面

在src/main/webapp下创建一个名为list.jsp的权限控制页面。

html 复制代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
  <title>用户列表</title>
</head>
<body>
  <table>
    <tr>
      <th>编号</th>
      <th>用户名</th>
      <th>姓名</th>
      <th>操作</th>
    </tr>
    <c:forEach items="${userList}" var="user">
      <tr>
        <td>${user.id}</td>
        <td>${user.username}</td>
        <td>${user.name}</td>
        <td>
          <a href="/user/edit/${user.id}" ${hasPermission('user:edit') ? '' : 'style="display:none;"'}>编辑</a>
          <a href="/user/delete/${user.id}" ${hasPermission('user:delete') ? '' : 'style="display:none;"'}>删除</a>
        </td>
      </tr>
    </c:forEach>
  </table>
  <a href="/user/add" ${hasPermission('user:add') ? '' : 'style="display:none;"'}>新增</a>
</body>
</html>

8、配置ehcache.xml

在src/main/resources下创建一个名为ehcache.xml的Ehcache配置文件,并配置缓存。

xml 复制代码
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd" 
         updateCheck="false" 
         name="myCacheManager">
    <defaultCache eternal="false" 
                  maxElementsInMemory="10000" 
                  overflowToDisk="true" 
                  diskPersistent="true" 
                  timeToIdleSeconds="120" 
                  timeToLiveSeconds="1200" 
                  diskExpiryThreadIntervalSeconds="120"/>
    
    <cache name="myCache1" 
           maxElementsInMemory="5000" 
           eternal="false" 
           overflowToDisk="true" 
           timeToIdleSeconds="120" 
           timeToLiveSeconds="1200" 
           diskExpiryThreadIntervalSeconds="120"/>
           
    <cache name="myCache2" 
           maxElementsInMemory="10000" 
           eternal="false" 
           overflowToDisk="true" 
           timeToIdleSeconds="240" 
           timeToLiveSeconds="2400" 
           diskExpiryThreadIntervalSeconds="240"/>
</ehcache>

9、编写Shiro配置文件

在src/main/resources下创建一个名为shiro.ini的Shiro配置文件,并配置相关信息。

xml 复制代码
[main]
# 配置Realm,用于身份认证和授权
userRealm = org.apache.shiro.realm.jdbc.JdbcRealm
userRealm.dataSource = $dataSource
userRealm.permissionsLookupEnabled = true
userRealm.authenticationQuery = SELECT password FROM user WHERE username = ?
userRealm.userRolesQuery = SELECT role_name FROM user_role WHERE username = ?
userRealm.permissionsQuery = SELECT permission FROM role_permission WHERE role_name = ?
userRealm.saltStyle = COLUMN

# 配置缓存
cacheManager = org.apache.shiro.cache.ehcache.EhCacheManager
cacheManager.cacheManagerConfigFile = classpath:ehcache.xml

# 配置Session管理器
sessionManager = org.apache.shiro.web.session.mgt.DefaultWebSessionManager
sessionManager.globalSessionTimeout = 1800000
sessionManager.deleteInvalidSessions = true

# 配置Cookie管理器
cookie = org.apache.shiro.web.servlet.SimpleCookie
cookie.name = JSESSIONID
cookie.path = /
cookie.httpOnly = true
cookie.maxAge = 1800000

# 配置SecurityManager,管理所有的Subject
securityManager = org.apache.shiro.web.mgt.DefaultWebSecurityManager
securityManager.realm = $userRealm
securityManager.cacheManager = $cacheManager
securityManager.sessionManager = $sessionManager
securityManager.sessionManager.sessionIdCookie = $cookie

# 配置过滤器,用于拦截请求
authc = org.apache.shiro.web.filter.authc.FormAuthenticationFilter
authc.loginUrl = /login
authc.successUrl = /user/list
authc.usernameParam = username
authc.passwordParam = password
authc.rememberMeParam = rememberMe
authc.rememberMeCookie = $cookie

perms = org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter

# 配置ShiroFilter,用于管理过滤器链
shiroFilter = org.apache.shiro.web.servlet.ShiroFilter
shiroFilter.securityManager = $securityManager
shiroFilter.loginUrl = /login
shiroFilter.filterChainDefinitions = /user/list = authc, perms["user:view"]
/user/add = authc, perms["user:add"]
/user/edit/** = authc, perms["user:edit"]
/user/delete/** = authc, perms["user:delete"]
/** = anon

[urls]
# 配置不需要拦截的URL
/login = anon
/logout = anon

10、配置Spring容器

在src/main/resources下创建一个名为spring-context.xml的Spring配置文件,并配置相关信息。

xml 复制代码
<beans>
  <!-- 配置数据源 -->
  <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="${jdbc.driverClassName}" />
    <property name="url" value="${jdbc.url}" />
    <property name="username" value="${jdbc.username}" />
    <property name="password" value="${jdbc.password}" />
  </bean>

  <!-- 配置MyBatis -->
  <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource" />
    <property name="typeAliasesPackage" value="com.example.demo.entity" />
    <property name="mapperLocations" value="classpath:mapper/*.xml" />
  </bean>

  <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="com.example.demo.dao" />
</bean>
 <!-- 配置事务管理器 -->
  <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
  </bean>
  <!-- 配置Shiro -->
  <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    <property name="securityManager" ref="securityManager" />
    <property name="loginUrl" value="/login" />
    <property name="filterChainDefinitions">
      <value>
        /user/list = authc, perms["user:view"]
        /user/add = authc, perms["user:add"]
        /user/edit/** = authc, perms["user:edit"]
        /user/delete/** = authc, perms["user:delete"]
        /** = anon
      </value>
    </property>
  </bean>
   <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
  <bean id="defaultAdvisorAutoProxyCreator" class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
    <property name="securityManager" ref="securityManager" />
  </bean>
  <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    <property name="realm" ref="jdbcRealm" />
    <property name="sessionManager" ref="sessionManager" />
    <property name="cacheManager" ref="cacheManager" />
  </bean>
  <bean id="jdbcRealm" class="org.apache.shiro.realm.jdbc.JdbcRealm">
    <property name="dataSource" ref="dataSource" />
    <property name="authenticationQuery" value="SELECT password FROM user WHERE username = ?" />
      <property name="userRolesQuery" value="SELECT role_name FROM user_role WHERE username = ?" />
    <property name="permissionsQuery" value="SELECT permission FROM role_permission WHERE role_name = ?" />
    <property name="saltStyle" value="COLUMN" />
  </bean>
  <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
    <property name="cacheManagerConfigFile" value="classpath:ehcache.xml" />
  </bean>
  <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
    <property name="globalSessionTimeout" value="1800000" />
    <property name="deleteInvalidSessions" value="true" />
    <property name="sessionValidationSchedulerEnabled" value="false" />
    <property name="sessionIdCookie" ref="simpleCookie" />
  </bean>
  <bean id="simpleCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
    <constructor-arg value="JSESSIONID" />
    <property name="httpOnly" value="true" />
    <property name="maxAge" value="1800000" />
    <property name="path" value="/" />
     </bean>
  <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />
  <bean id="defaultAdvisorAutoProxyCreator" class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
    <property name="securityManager" ref="securityManager" />
  </bean>
  <bean id="authorizationAttributeSourceAdvisor" class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
    <property name="securityManager" ref="securityManager" />
  </bean>
</beans>

11、编写Shiro的Realm

Shiro通过Realm来进行认证和授权操作。在这里,我们使用JdbcRealm来与数据库进行交互。

  • 1、创建JdbcRealm类
java 复制代码
package com.example.demo.shiro;

import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.subject.PrincipalCollection;

import java.util.HashSet;
import java.util.Set;

public class MyJdbcRealm extends JdbcRealm {

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken upToken = (UsernamePasswordToken) token;
        String username = upToken.getUsername();
        String password = getPasswordForUser(username);

        if (password == null) {
            throw new UnknownAccountException("No account found for user [" + username + "]");
        }

        return new SimpleAuthenticationInfo(username, password.toCharArray(), getName());
    }

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        String username = (String) principals.getPrimaryPrincipal();

        Set<String> roles = getRolesForUser(username);
        Set<String> permissions = getPermissionsForUser(username);

        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.setRoles(roles);
        authorizationInfo.setStringPermissions(permissions);

        return authorizationInfo;
    }

    private Set<String> getPermissionsForUser(String username) {
        Set<String> permissions = new HashSet<>();
        // TODO: 获取该用户的所有权限,并加入到permissions集合中
        return permissions;
    }

    private Set<String> getRolesForUser(String username) {
        Set<String> roles = new HashSet<>();
        // TODO: 获取该用户的所有角色,并加入到roles集合中
        return roles;
    }

    private String getPasswordForUser(String username) {
        // TODO: 根据用户名从数据库中获取密码
        return null;
    }
}
  • 2、配置JdbcRealm 在applicationContext.xml文件中配置JdbcRealm。
xml 复制代码
<bean id="jdbcRealm" class="com.example.demo.shiro.MyJdbcRealm">
    <property name="dataSource" ref="dataSource" />
    <property name="authenticationQuery" value="SELECT password FROM user WHERE username = ?" />
    <property name="userRolesQuery" value="SELECT role_name FROM user_role WHERE username = ?" />
    <property name="permissionsQuery" value="SELECT permission FROM role_permission WHERE role_name = ?" />
    <property name="saltStyle" value="COLUMN" />
</bean>

二、编写Shiro的过滤器

Shiro提供了许多过滤器,用于实现不同的功能。在这里,我们需要用到以下过滤器:

  • authc:需要进行身份验证才能访问
  • perms:需要具有某种权限才能访问
  • anon:不需要进行身份验证就能访问

1、(bug)创建ShiroFilterFactoryBean类

在com.example.demo.shiro包下创建一个ShiroFilterFactoryBean类。

java 复制代码
package com.example.demo.shiro;

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;

import java.util.LinkedHashMap;
import java.util.Map;

public class ShiroFilterFactoryBean extends ShiroFilterFactoryBean {

    // 配置拦截链规则
    @Override
    protected Map<String, String> createFilterChainDefinitionMap() {
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();

        // 配置登录的url和登录成功的url
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/doLogin", "anon");
        filterChainDefinitionMap.put("/logout", "logout");

        // 配置不会被拦截的链接
        filterChainDefinitionMap.put("/static/**", "anon");

        // 配置拦截规则
        filterChainDefinitionMap.put("/**", "authc");

        return filterChainDefinitionMap;
    }

    // 配置登录成功之后跳转的url
    public void setSuccessUrl(String successUrl) {
        super.setSuccessUrl(successUrl);
    }

    // 配置登录页面的url
    public void setLoginUrl(String loginUrl) {
        super.setLoginUrl(loginUrl);
        FormAuthenticationFilter filter = (FormAuthenticationFilter) super.getFilters().get("authc");
        filter.setLoginUrl(loginUrl);
    }
}

2、配置ShiroFilterFactoryBean

xml 复制代码
<bean id="shiroFilter" class="com.example.demo.shiro.MyShiroFilterFactoryBean">
    <property name="securityManager" ref="securityManager" />
    <property name="loginUrl" value="/login" />
    <property name="successUrl" value="/" />
    <property name="unauthorizedUrl" value="/unauthorized" />
    <property name="filterChainDefinitions">
        <value>
            /login = anon
            /logout = logout
            /admin/** = authc,perms[admin:manage]
            /** = authc
        </value>
    </property>
</bean>

三、配置Spring MVC

在Spring MVC中,需要配置一个拦截器,用于在每个请求中进行身份验证和授权操作。

1、创建ShiroInterceptor类

在com.example.demo.shiro包下创建一个ShiroInterceptor类。

java 复制代码
package com.example.demo.shiro;

import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ShiroInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String requestUri = request.getRequestURI();
        if (requestUri.equals("/login") || requestUri.equals("/doLogin")) {
            return true;
        }

        Subject subject = SecurityUtils.getSubject();
        if (!subject.isAuthenticated()) {
            response.sendRedirect("/login");
            return false;
        }

        String permission = getPermission(requestUri);
        if (!subject.isPermitted(permission)) {
            response.sendRedirect("/unauthorized");
            return false;
        }

        return true;
    }

    private String getPermission(String requestUri) {
        // TODO: 根据请求URI获取相应的权限字符串
        return "";
    }
}

2、配置ShiroInterceptor

在spring-mvc.xml文件中配置ShiroInterceptor。

xml 复制代码
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/**" />
        <bean class="com.example.demo.shiro.ShiroInterceptor" />
    </mvc:interceptor>
</mvc:interceptors>

至此,SSM框架整合Shiro的配置就完成了。可以启动应用程序并尝试访问受保护的URL来测试配置是否正确。

四、使用Shiro

1、创建用户登录页面

在src/main/resources/templates目录下创建一个名为login.html的文件,用于用户登录。

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
</head>
<body>
    <form method="post" action="/doLogin">
        <label>用户名:</label>
        <input type="text" name="username">
        <br>
        <label>密码:</label>
        <input type="password" name="password">
        <br>
        <input type="submit" value="登录">
    </form>
</body>
</html>

2、创建未授权页面

在src/main/resources/templates目录下创建一个名为unauthorized.html的文件,用于在未经授权的情况下拒绝访问。

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>未授权页面</title>
</head>
<body>
    <h1>对不起,您无权访问该页面!</h1>
</body>
</html>

3、创建受保护的页面

在src/main/resources/templates目录下创建一个名为admin.html的文件,用于测试受保护的页面。

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>管理员页面</title>
</head>
<body>
    <h1>欢迎来到管理员页面!</h1>
</body>
</html>

4、创建Controller

在com.example.demo.controller包下创建一个名为AdminController的控制器类。

java 复制代码
package com.example.demo.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/admin")
public class AdminController {

    @RequestMapping("/index")
    public String index() {
        return "admin";
    }
}

5、测试

启动应用程序并尝试访问以下URL:

/login:进入登录页面; /admin/index:进入受保护的页面,需要登录和具有admin:manage权限才能访问; /unauthorized:访问未经授权的页面。 在登录页面输入用户名和密码,可以成功登录并进入受保护的页面;在未经授权的情况下访问受保护的页面,会重定向到未授权页面。

五、自定义Realm

在前面的示例中,我们使用了IniRealm来实现身份验证和授权,这种方式非常简单,但是不太灵活。在实际应用中,我们可能需要自定义Realm来适应特定的需求。

1、创建自定义Realm

在com.example.demo.shiro包下创建一个名为CustomRealm的类,继承AuthorizingRealm类并实现其中的两个方法。

java 复制代码
package com.example.demo.shiro;

import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

public class CustomRealm extends AuthorizingRealm {

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        if (principals == null) {
            throw new AuthorizationException("PrincipalCollection method argument cannot be null.");
        }
        String username = (String) getAvailablePrincipal(principals);
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        if ("admin".equals(username)) {
            info.addRole("admin");
            info.addStringPermission("admin:manage");
        }
        return info;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        String username = (String) token.getPrincipal();
        String password = new String((char[]) token.getCredentials());
        if (!"admin".equals(username)) {
            throw new UnknownAccountException("Unknown account " + username);
        }
        if (!"123456".equals(password)) {
            throw new IncorrectCredentialsException("Incorrect password for account " + username);
        }
        return new SimpleAuthenticationInfo(username, password, getName());
    }
}

上述代码中的doGetAuthenticationInfo()方法用于实现身份验证,其实现方式与前面的示例类似;doGetAuthorizationInfo()方法用于实现授权,这里根据用户名判断用户是否为管理员,如果是,则为其添加一个名为admin的角色和一个名为admin:manage的权限。

2、在Shiro中使用自定义Realm

修改ShiroConfig类中的securityManager()方法,将其中的IniRealm替换为CustomRealm。

java 复制代码
@Bean
public SecurityManager securityManager() {
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    securityManager.setRealm(customRealm());
    return securityManager;
}

@Bean
public CustomRealm customRealm() {
    return new CustomRealm();
}

现在,我们已经成功创建了一个自定义的Realm并将其集成到Shiro中,可以重新启动应用程序并进行测试。

3、测试

重新启动应用程序并尝试访问以下URL:

/login:进入登录页面; /admin/index:进入受保护的页面,需要登录和具有admin:manage权限才能访问; /unauthorized:访问未经授权的页面。 在登录页面输入用户名和密码,可以成功登录并进入受保护的页面;在未经授权的情况下访问受保护的页面,会重定向到未授权页面。

六、结语

本文介绍了如何将Shiro集成到SSM框架中,并通过一个简单的示例演示了Shiro的身份验证和授权功能,以及如何自定义Realm来实现特定的需求。

当然,Shiro的功能远不止于此,它还提供了许多其他的功能和特性,如Session管理、RememberMe功能、加密和解密、过滤器等,读者可以参考官方文档深入了解。同时,由于Shiro的灵活性和可扩展性,也可以通过扩展其他组件,如Cache、Realms、SessionDAO等,来满足更多的需求。

在实际开发中,安全性通常是不容忽视的一个问题,Shiro作为一个功能丰富、易于使用、灵活可扩展的安全框架,在保护应用程序方面具有重要作用。

相关推荐
Yyyyyy~2 分钟前
【java】数组的定义和使用
java
麻辣布丁2 分钟前
java集合篇面试模拟
java·开发语言·面试
niucloud-admin4 分钟前
JAVA V6 多商户商城 开发文档——插件开发规范
java·开发语言
嘟哩DuliDuli17 分钟前
创作 Agent 的任务状态该如何保存
android·java·javascript
wdfk_prog29 分钟前
lely-canopen-rtt:为什么推荐在 RT-Thread 上使用 Lely CANopen 构建主站
java·运维·缓存·docker·容器
Token掘金室35 分钟前
JSON模式结构化输出报错
java·服务器·json
秋饼40 分钟前
Spring AI 2.0 接入 DeepSeek V4.1 Flash 生产级实战
java·ai·技术分享·后端开发
IT枫斗者枫哥41 分钟前
Java 分批导出仍然 OOM?用 32 MiB 堆复现三种 CSV 写法
java
shehuiyuelaiyuehao1 小时前
算法43,外观数列,模拟算法+双指针
java·算法
lhldsg1 小时前
多商户团购系统源码:技术架构选型与二次开发实战指南
java·小程序·架构