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