Apache Shiro 是一个强大且灵活的 Java 安全框架,用于身份验证、授权、加密和会话管理。要定制 Shiro 的认证流程,你需要遵循以下步骤:
1. 引入依赖
首先,在你的项目中引入 Apache Shiro 的依赖。如果你使用的是 Maven,可以在 pom.xml
文件中添加以下依赖:
org.apache.shiro shiro-core 1.8.0 org.apache.shiro shiro-spring 1.8.0
2. 配置 Shiro
创建一个 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(); // 配置数据源 jdbcRealm.setDataSource(dataSource()); // 配置 SQL 语句 jdbcRealm.setConnectionCheckSQL("SELECT 1"); jdbcRealm.setPermissionsLookupEnabled(true); return jdbcRealm; } @Bean public DataSource dataSource() { // 配置数据源,例如使用 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); // 配置过滤器链 MapfilterChainDefinitionMap = new LinkedHashMap<>(); filterChainDefinitionMap.put("/admin/**", "authc"); // 需要认证的路由 filterChainDefinitionMap.put("/**", "anon"); // 不需要认证的路由 shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; } }
3. 自定义 Realm
在上面的配置中,JdbcRealm
是用于从数据库中获取用户信息和权限的。你可以通过继承 AuthorizingRealm
来实现自定义的认证和授权逻辑。
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(); // 查询用户的角色和权限 Setroles = getRolesForUser(username); Set 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 getRolesForUser(String username) { // 实现获取用户角色的逻辑 return new HashSet<>(); } private Set getPermissionsForUser(String username) { // 实现获取用户权限的逻辑 return new HashSet<>(); } private User getUserByUsername(String username) { // 实现从数据库中获取用户的逻辑 return new User(); } }
4. 自定义过滤器
Shiro 允许你自定义过滤器来实现特定的认证流程。你可以通过继承 org.apache.shiro.web.filter.authc.AuthenticatingFilter
来实现自定义的过滤器。
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); // 配置过滤器链 MapfilterChainDefinitionMap = new LinkedHashMap<>(); filterChainDefinitionMap.put("/admin/**", "customAuthc"); // 使用自定义过滤器 filterChainDefinitionMap.put("/**", "anon"); // 不需要认证的路由 shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); return shiroFilterFactoryBean; }
5. 自定义认证逻辑
在 CustomAuthenticatingFilter
中,你可以实现自定义的认证逻辑,例如添加额外的验证步骤、处理不同的认证方式(如短信验证码、邮箱验证等)。
通过以上步骤,你可以定制 Shiro 的认证流程,以满足你的项目需求。