Apache Shiro 是一個強大且靈活的 Java 安全框架,用于身份驗證、授權、加密和會話管理。要定制 Shiro 的認證流程,你需要遵循以下步驟:
首先,在你的項目中引入 Apache Shiro 的依賴。如果你使用的是 Maven,可以在 pom.xml
文件中添加以下依賴:
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.8.0</version>
</dependency>
創(chuàng)建一個 Shiro 配置類,通常命名為 ShiroConfig
。在這個類中,你需要配置 Shiro 的各個組件,包括 SecurityManager
、Realm
、Authenticator
、Authorizer
和 SessionManager
。
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.jdbc.JdbcRealm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ShiroConfig {
@Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(jdbcRealm());
return securityManager;
}
@Bean
public JdbcRealm jdbcRealm() {
JdbcRealm jdbcRealm = new JdbcRealm();
// 配置數(shù)據(jù)源
jdbcRealm.setDataSource(dataSource());
// 配置 SQL 語句
jdbcRealm.setConnectionCheckSQL("SELECT 1");
jdbcRealm.setPermissionsLookupEnabled(true);
return jdbcRealm;
}
@Bean
public DataSource dataSource() {
// 配置數(shù)據(jù)源,例如使用 HikariCP
HikariDataSource dataSource = new HikariDataSource();
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/mydb");
dataSource.setUsername("username");
dataSource.setPassword("password");
return dataSource;
}
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 配置過濾器鏈
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/admin/**", "authc"); // 需要認證的路由
filterChainDefinitionMap.put("/**", "anon"); // 不需要認證的路由
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
}
在上面的配置中,JdbcRealm
是用于從數(shù)據(jù)庫中獲取用戶信息和權限的。你可以通過繼承 AuthorizingRealm
來實現(xiàn)自定義的認證和授權邏輯。
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import java.util.HashSet;
import java.util.Set;
public class CustomRealm extends AuthorizingRealm {
@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;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
String username = upToken.getUsername();
// 查詢用戶信息
User user = getUserByUsername(username);
if (user == null) {
throw new UnknownAccountException("用戶不存在");
}
return new SimpleAuthenticationInfo(user.getUsername(), user.getPassword(), getName());
}
private Set<String> getRolesForUser(String username) {
// 實現(xiàn)獲取用戶角色的邏輯
return new HashSet<>();
}
private Set<String> getPermissionsForUser(String username) {
// 實現(xiàn)獲取用戶權限的邏輯
return new HashSet<>();
}
private User getUserByUsername(String username) {
// 實現(xiàn)從數(shù)據(jù)庫中獲取用戶的邏輯
return new User();
}
}
Shiro 允許你自定義過濾器來實現(xiàn)特定的認證流程。你可以通過繼承 org.apache.shiro.web.filter.authc.AuthenticatingFilter
來實現(xiàn)自定義的過濾器。
import org.apache.shiro.authc.*;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
public class CustomAuthenticatingFilter extends AuthenticatingFilter {
@Override
protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception {
String username = getUsername(request);
String password = getPassword(request);
return new UsernamePasswordToken(username, password);
}
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
return executeLogin(request, response);
}
@Override
protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
return false;
}
@Override
protected void executeLogin(ServletRequest request, ServletResponse response) throws Exception {
try {
getSubject(request, response).login(createToken(request, response));
} catch (AuthenticationException e) {
// 處理認證失敗的情況
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write("{\"status\":\"error\",\"message\":\"認證失敗\"}");
}
}
}
然后在 ShiroFilterFactoryBean
中配置這個自定義過濾器:
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 配置過濾器鏈
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/admin/**", "customAuthc"); // 使用自定義過濾器
filterChainDefinitionMap.put("/**", "anon"); // 不需要認證的路由
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
在 CustomAuthenticatingFilter
中,你可以實現(xiàn)自定義的認證邏輯,例如添加額外的驗證步驟、處理不同的認證方式(如短信驗證碼、郵箱驗證等)。
通過以上步驟,你可以定制 Shiro 的認證流程,以滿足你的項目需求。