SSM框架整合Shiro

2023年 8月 7日 50.5k 0

一、环境准备

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


  
  
    org.apache.shiro
    shiro-core
    1.7.1
  
  
    org.apache.shiro
    shiro-spring
    1.7.1
  
  
  
  
    org.springframework
    spring-webmvc
    5.3.14
  
  
    org.springframework
    spring-jdbc
    5.3.14
  
  
    org.springframework
    spring-context-support
    5.3.14
  
  
  
  
    org.mybatis
    mybatis
    3.5.7
  
  
    org.mybatis
    mybatis-spring
    2.0.7
  
  
  
  
    mysql
    mysql-connector-java
    8.0.27
  


2、配置数据源和MyBatis

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



  
  
  
  




  
  
  



  


3、配置Shiro

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

[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

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 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的接口,并定义获取用户信息和权限的方法。

public interface UserService {
  User getUserByUsername(String username);
  List getPermissionsByUserId(Long userId);
}

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

@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 getPermissionsByUserId(Long userId) {
    return permissionMapper.getPermissionsByUserId(userId);
  }
}

5、编写Controller

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

@Controller
@RequestMapping("/user")
public class UserController {

  @Autowired
private UserService userService;

@RequestMapping("/list")
@RequiresPermissions("user:view")
public String list(Model model) {
List 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的登录页面。




  登录


  
    
    
    记住我
    登录
  


7、编写权限控制页面

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




  用户列表


  
    
      编号
      用户名
      姓名
      操作
    
    
      
        ${user.id}
        ${user.username}
        ${user.name}
        
          编辑
          删除
        
      
    
  
  新增



8、配置ehcache.xml

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


    
    
    
           
    


9、编写Shiro配置文件

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

[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配置文件,并配置相关信息。


  
  
    
    
    
    
  

  
  
    
    
    
  

  
    

 
  
    
  
  
  
    
    
    
      
        /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
      
    
  
   
  
    
  
  
    
    
    
  
  
    
    
      
    
    
  
  
    
  
  
    
    
    
    
  
  
    
    
    
    
     
  
  
    
  
  
    
  


11、编写Shiro的Realm

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

  • 1、创建JdbcRealm类
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 roles = getRolesForUser(username);
        Set permissions = getPermissionsForUser(username);

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

        return authorizationInfo;
    }

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

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

    private String getPasswordForUser(String username) {
        // TODO: 根据用户名从数据库中获取密码
        return null;
    }
}

  • 2、配置JdbcRealm
    在applicationContext.xml文件中配置JdbcRealm。

    
    
    
    
    


二、编写Shiro的过滤器

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

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

1、(bug)创建ShiroFilterFactoryBean类

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

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 createFilterChainDefinitionMap() {
        Map 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


    
    
    
    
    
        
            /login = anon
            /logout = logout
            /admin/** = authc,perms[admin:manage]
            /** = authc
        
    


三、配置Spring MVC

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

1、创建ShiroInterceptor类

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

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。


    
        
        
    


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

四、使用Shiro

1、创建用户登录页面

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




    
    登录页面


    
        用户名:
        
        
        密码:
        
        
        
    



2、创建未授权页面

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




    
    未授权页面


    对不起,您无权访问该页面!



3、创建受保护的页面

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




    
    管理员页面


    欢迎来到管理员页面!



4、创建Controller

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

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类并实现其中的两个方法。

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。

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

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论