首先,我们需要创建一个Spring Boot项目,并添加必要的依赖。在pom.xml
文件中添加Shiro、MyBatis、数据库驱动和Thymeleaf等依赖:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
创建一个数据库和用户表,用于存储用户信息。例如,一个简单的用户表结构如下:
CREATE TABLE user ( |
|
id INT AUTO_INCREMENT PRIMARY KEY, |
|
username VARCHAR(50) NOT NULL UNIQUE, |
|
password VARCHAR(100) NOT NULL, |
|
salt VARCHAR(50), |
|
realname VARCHAR(100) |
|
); |
然后,在Spring Boot项目中创建一个对应的实体类User
,并使用JPA注解进行映射:
import javax.persistence.*; |
|
@Entity |
|
@Table(name = "user") |
|
public class User { |
|
@Id |
|
@GeneratedValue(strategy = GenerationType.IDENTITY) |
|
private Integer id; |
|
@Column(name = "username") |
|
private String username; |
|
@Column(name = "password") |
|
private String password; |
|
@Column(name = "salt") |
|
private String salt; |
|
@Column(name = "realname") |
|
private String realname; |
|
// Getters and Setters |
|
} |
创建一个Shiro配置类ShiroConfig
,配置Shiro的安全管理器、Realm和过滤器链:
import org.apache.shiro.authc.credential.HashedCredentialsMatcher; |
|
import org.apache.shiro.mgt.SecurityManager; |
|
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; |
|
import java.util.LinkedHashMap; |
|
import java.util.Map; |
|
@Configuration |
|
public class ShiroConfig { |
|
@Bean |
|
public SecurityManager securityManager() { |
|
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); |
|
securityManager.setRealm(myRealm()); |
|
return securityManager; |
|
} |
|
@Bean |
|
public MyRealm myRealm() { |
|
MyRealm realm = new MyRealm(); |
|
realm.setCredentialsMatcher(credentialsMatcher()); |
|
return realm; |
|
} |
|
@Bean |
|
public HashedCredentialsMatcher credentialsMatcher() { |
|
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher(); |
|
matcher.setHashAlgorithmName("SHA-256"); |
|
matcher.setHashIterations(1024); |
|
return matcher; |
|
} |
|
@Bean |
|
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { |
|
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); |
|
shiroFilterFactoryBean.setSecurityManager(securityManager); |
|
shiroFilterFactoryBean.setLoginUrl("/login"); |
|
shiroFilterFactoryBean.setSuccessUrl("/"); |
|
shiroFilterFactoryBean.setUnauthorizedUrl("/403"); |
|
Map |
|
filterChainDefinitionMap.put("/css/**", "anon"); |
|
filterChainDefinitionMap.put("/js/**", "anon"); |
|
filterChainDefinitionMap.put("/fonts/**", "anon"); |
|
filterChainDefinitionMap.put("/img/**", "anon"); |
|
filterChainDefinitionMap.put("/login", "anon"); |
|
filterChainDefinitionMap.put("/logout", "logout"); |
|
filterChainDefinitionMap.put("/**", "authc"); |
|
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); |
|
return shiroFilterFactoryBean; |
|
} |
|
} |
创建一个自定义的Realm类MyRealm
,用于处理用户的认证和授权逻辑:
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 org.apache.shiro.util.ByteSource; |
|
import org.springframework.beans.factory.annotation.Autowired; |
|
public class MyRealm extends AuthorizingRealm { |
|
@Autowired |
|
private UserService userService; |
|
@Override |
|
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { |
|
String username = (String) principals.getPrimaryPrincipal(); |
|
User user = userService.findByUsername(username); |
|
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); |
|
authorizationInfo.addRoles(user.getRoles()); |
|
authorizationInfo.addStringPermissions(user.getPermissions()); |
|
return authorizationInfo; |
|
} |
|
@Override |
|
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { |
|
UsernamePasswordToken upToken = (UsernamePasswordToken) token; |
|
String username = upToken.getUsername(); |
|
User user = userService.findByUsername(username); |
|
if (user == null) { |
|
throw new UnknownAccountException("用户不存在"); |
|
} |
|
ByteSource salt = ByteSource.Util.bytes(user.getSalt()); |
|
return new SimpleAuthenticationInfo(username, user.getPassword(), salt, getName()); |
|
} |
|
} |
创建一个UserService
接口和实现类,用于访问数据库中的用户信息:
import org.springframework.stereotype.Service; |
|
import java.util.List; |
|
public interface UserService { |
|
User findByUsername(String username); |
|
// 其他方法... |
|
} |
|
@Service |
|
public class UserServiceImpl implements UserService { |
|
@Autowired |
|
private UserMapper userMapper; |
|
@Override |
|
public User findByUsername(String username) { |
|
List |
|
for (User user : users) { |
|
if (user.getUsername().equals(username)) { |
|
return user; |
|
} |
|
} |
|
return null; |
|
} |
|
// 其他实现... |
|
} |
创建一个UserMapper
接口和对应的XML映射文件,用于MyBatis操作数据库:
import org.apache.ibatis.annotations.Mapper; |
|
import java.util.List; |
|
@Mapper |
|
public interface UserMapper { |
|
List |
|
} |
UserMapper.xml
:
|
|
|
|
|
|
|
|
SELECT * FROM user |
|
|
|
|
创建一个控制器类,用于处理登录请求和受保护的资源请求:
package com.bdqn.controller; import com.bdqn.pojo.Right; import com.bdqn.pojo.Role; import com.bdqn.pojo.User; import com.bdqn.service.RoleService; import com.bdqn.service.UserService; import jakarta.annotation.Resource; import jakarta.servlet.http.HttpSession; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.*; import org.apache.shiro.subject.Subject; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import java.util.List; /** * 登录注销相关控制器 * * @author LILIBO * @since 2024/9/21 */ @Controller public class IndexController { @Resource private UserService userService; @Resource private RoleService roleService; /** * 去登录页 */ @GetMapping("/login") public String toLogin() { return "login"; } /** * 执行登录操作 */ @PostMapping("/login") public String doLogin(HttpSession session, String usrName, String usrPassword, Model model) { // User loginUser = userService.login(usrName, usrPassword); // session.setAttribute("loginUser", loginUser); // return "redirect:/main"; try { UsernamePasswordToken token=new UsernamePasswordToken(usrName,usrPassword); Subject subject= SecurityUtils.getSubject(); subject.login(token); // 认证(登录)成功 User user=(User) subject.getPrincipal(); Role role = user.getRole(); Listrights = roleService.getRigthsByRoleId(role); role.getRights().addAll(rights); session.setAttribute("loginUser",user); return "redirect:/main"; }catch (UnknownAccountException | IncorrectCredentialsException e){ model.addAttribute("message","用户名或密码失败,登录失败!"); return "login"; }catch (LockedAccountException e){ model.addAttribute("message","用户被禁用,登录失败!"); return "login"; }catch (AuthenticationException e){ model.addAttribute("message","认证异常,登录失败!"); return "login"; } } /** * 跳转到主页 */ @GetMapping("/main") public String toMain() { return "main"; } /** * 退出登录 */ @GetMapping("/logout") public String logout(HttpSession session) { session.removeAttribute("loginUser"); SecurityUtils.getSubject().logout(); return "redirect:/login"; } @GetMapping("/403") public String to403(){ return "403"; } }