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作为一个功能丰富、易于使用、灵活可扩展的安全框架,在保护应用程序方面具有重要作用。

相关推荐
n北斗5 分钟前
常用类晨考day15
java
骇客野人8 分钟前
【JAVA】JAVA接口公共返回体ResponseData封装
java·开发语言
yuanbenshidiaos1 小时前
c++---------数据类型
java·jvm·c++
向宇it1 小时前
【从零开始入门unity游戏开发之——C#篇25】C#面向对象动态多态——virtual、override 和 base 关键字、抽象类和抽象方法
java·开发语言·unity·c#·游戏引擎
Lojarro1 小时前
【Spring】Spring框架之-AOP
java·mysql·spring
莫名其妙小饼干2 小时前
网上球鞋竞拍系统|Java|SSM|VUE| 前后端分离
java·开发语言·maven·mssql
isolusion2 小时前
Springboot的创建方式
java·spring boot·后端
zjw_rp2 小时前
Spring-AOP
java·后端·spring·spring-aop
Oneforlove_twoforjob2 小时前
【Java基础面试题033】Java泛型的作用是什么?
java·开发语言
TodoCoder3 小时前
【编程思想】CopyOnWrite是如何解决高并发场景中的读写瓶颈?
java·后端·面试