认证
固定数据
Realm
@Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { String principal = (String) authenticationToken.getPrincipal(); if("admin".equals(principal)){ return new SimpleAuthenticationInfo("","123",this.getName()); } return null; }
配置类
配置类中设置登录路径
//指定登录页面 shiroFilterFactoryBean.setLoginUrl("/tologin");
放行登录操作的URL
filterChainDefinitionMap.put("/login", "anon");
自定义登录页面
html
<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form th:action="@{/login}" > 用户名:<input type="text" name="username"/> 密码:<input type="password" name="password"/> <input type="submit" value="登录"/> </form> </body> </html>
controller
@GetMapping("/tologin") public String tologin(){ return "login"; }
登录操作
@RequestMapping("/login") public String login(String username,String password){ Subject subject = SecurityUtils.getSubject(); try { subject.login(new UsernamePasswordToken(username,password)); return "index"; } catch (AuthenticationException e) { e.printStackTrace(); return "login"; } }
数据库数据
数据准备
CREATE TABLE `t_user` ( `USER_ID` BIGINT(0) NOT NULL AUTO_INCREMENT COMMENT '用户ID', `USERNAME` VARCHAR(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '登录名', `NAME` VARCHAR(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '用户名', `PASSWORD` VARCHAR(128) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '密码', `EMAIL` VARCHAR(128) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '邮箱', `MOBILE` VARCHAR(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '联系电话', `STATUS` CHAR(1) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '状态 0无效 1有效', `LAST_LOGIN_TIME` DATETIME(0) NULL DEFAULT NULL COMMENT '最近访问时间', `SSEX` CHAR(1) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '性别 0男 1女 2保密', `DESCRIPTION` VARCHAR(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '描述', `AVATAR` VARCHAR(100) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '用户头像', `FIRST_LANDING` CHAR(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '0' COMMENT '首次登陆(0:首次登陆,1:非首次登陆)', `ROLE_ID` BIGINT(0) NULL DEFAULT NULL COMMENT '角色id', `HOSPITAL_PERM` VARCHAR(1) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '医院参照权限(1:全国;2:省指定;3:市指定;4:医院指定)', `OPEN_ID` VARCHAR(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '微信推送ID', `DELETE_FLAG` TINYINT(0) NOT NULL COMMENT '删除标志', `CREATE_TIME` DATETIME(0) NOT NULL COMMENT '创建时间', `CREATE_USER` VARCHAR(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '创建人', `MODIFY_TIME` DATETIME(0) NOT NULL COMMENT '修改时间', `MODIFY_USER` VARCHAR(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL COMMENT '修改人', PRIMARY KEY (`USER_ID`) USING BTREE, INDEX `t_user_index1`(`USER_ID`, `NAME`, `USERNAME`, `MOBILE`, `STATUS`, `CREATE_TIME`) USING BTREE ) ENGINE = INNODB AUTO_INCREMENT = 68 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = COMPACT; INSERT INTO `t_user`(`USER_ID`, `USERNAME`, `NAME`, `PASSWORD`, `EMAIL`, `MOBILE`, `STATUS`, `LAST_LOGIN_TIME`, `SSEX`, `DESCRIPTION`, `AVATAR`, `FIRST_LANDING`, `ROLE_ID`, `HOSPITAL_PERM`, `OPEN_ID`, `DELETE_FLAG`, `CREATE_TIME`, `CREATE_USER`, `MODIFY_TIME`, `MODIFY_USER`) VALUES (1, 'admin', '超级管理员', '202cb962ac59075b964b07152d234b70', 'aaa@hotmail.com', '17314941691', '1', '2021-07-09 16:17:56', '2', '超级管理员', '20180414170003.jpg', '1', 1, NULL, NULL, 0, '2017-12-27 15:47:19', 'sys', '2021-07-05 09:40:44', 'admin');
密码:123的md5加密
添加依赖
shiro依赖、数据库驱动依赖、mybatis-plus依赖、生成器依赖(注意:模板依赖)
<dependencies> <!--shiro-spring 依赖 --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.7.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!--mysql 依赖 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.26</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.20</version> </dependency> <!--mybatis-plus 依赖 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.2.0</version> </dependency> <!--mybatis-plus的生成器 依赖 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.4.1</version> </dependency> <!--mybatis-plus的生成器中模板 依赖 --> <dependency> <groupId>org.freemarker</groupId> <artifactId>freemarker</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
编写UserServiceImpl
@Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService { @Override public User findByName(String username) { return baseMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUsername, username)); } }
Realm
public class ShiroRealm extends AuthorizingRealm { @Autowired private IUserService userServiceImpl; /** * 认证 * @param authenticationToken * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { String principal = (String) authenticationToken.getPrincipal(); User user = userServiceImpl.findByName(principal); if(user!=null){ return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(),this.getName()); } return null; } /** * 授权 * @param principalCollection * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { return null; } }
ShiroRealm没有被标记为spring容器管理的bean,为什么可以@AutoWired属性注入呢?
原因是在ShiroConfig中,shiroRealm被Spring容器管理
@Configuration public class ShiroConfig { //ShiroFilterFactoryBean(过滤器工厂对象:初始化SecurityManager、请求处理) @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); // 设置 securityManager shiroFilterFactoryBean.setSecurityManager(securityManager); LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); filterChainDefinitionMap.put("/login", "anon"); // 所有请求都要认证 filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); //指定登录页面 shiroFilterFactoryBean.setLoginUrl("/tologin"); return shiroFilterFactoryBean; } //DefaultWebSecurityManager(对应SecurityManager对象) @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // 配置 SecurityManager,并注入 shiroRealm securityManager.setRealm(shiroRealm()); return securityManager; } //Realm 需要自定义 @Bean public ShiroRealm shiroRealm() { // 配置 Realm return new ShiroRealm(); } }
加密
在数据准备时密码是经过加密处理的,但是登录请求的密码是明文,密码通过equals方法继续比较的,所以比较之前需要把明文密码进行加密处理
shiro中密码比较默认匹配器是equals
在AuthenticatingRealm中的assertCredentialsMatch
可以看到getCredentialsMatcher方法获取匹配器,那么就可以设置匹配器不使用其默认匹配器。
在shiro的配置类中的Realm对象设置匹配器
//Realm 需要自定义 @Bean public ShiroRealm shiroRealm() { // 配置 Realm ShiroRealm shiroRealm = new ShiroRealm(); //修改密码匹配器 HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); hashedCredentialsMatcher.setHashAlgorithmName("MD5"); shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher); return shiroRealm; }