org.springframework.boot
spring-boot-starter-web
org.projectlombok
lombok
1.18.10
org.springframework.boot
spring-boot-devtools
true
true
mysql
mysql-connector-java
5.1.39
com.baomidou
mybatis-plus-boot-starter
3.5.1
org.springframework.boot
spring-boot-starter-thymeleaf
org.apache.shiro
shiro-spring
1.4.0
com.alibaba
druid
1.0.9
com.github.theborakompanioni
thymeleaf-extras-shiro
2.0.0
# 配置端口号
server:
port: 8888
spring:
# 配置数据源
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/study?useSSL=false
username: root
password: admin
# 配置数据库连接池
type: com.alibaba.druid.pool.DruidDataSource
#配置MyBatis-Plus
mybatis-plus:
# 配置别名
type-aliases-package: com.wx.springboot_shiro.entity
# 配置xxxMapper.xml文件地址
mapper-locations:
classpath: mapper/*.xml
数据库表中的字段与实体类字段相同
直接使用lombok注解来进行有参无参构造和Getter/Setter方法
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Long id;
private String name;
private String password;
}
在接口上加上@Mapper注解,或在启动类上添加@MapperScan("mapper接口所在路径")
因为使用的是MyBatis-Plus,所以直接继承BaseMapper<实体类名>即可
@Mapper
public interface UserMapper extends BaseMapper {
}
public interface UserService {
User getUserByName(String name);
}
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.wx.springboot_shiro.dao.UserMapper;
import com.wx.springboot_shiro.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author iuxin
* @date create in 20:33 2022/4/16
* @apiNote
*/
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public User getUserByName(String name) {
QueryWrapper userQueryWrapper = new QueryWrapper<>();
userQueryWrapper.eq("name",name);
return userMapper.selectOne(userQueryWrapper);
}
}
@Controller
public class UserController {
@RequestMapping("/testThymeleaf")
public String testThymeleaf(Model model){
model.addAttribute("name","Hello");
return "test";
}
@RequestMapping("/login")
public String login(String name,String password,Model model){
// 使用Shiro编写认证操作
// 1、获取Subject
Subject subject = SecurityUtils.getSubject();
// 2、封装用户数据
UsernamePasswordToken token = new UsernamePasswordToken(name, password);
// 3、执行登录方法
try {
subject.login(token);
// 登录成功跳转到test.html
return "redirect:/testThymeleaf";
}catch (UnknownAccountException e) {
// 登录失败,用户不存在
model.addAttribute("msg","用户不存在");
return "login";
}catch (IncorrectCredentialsException e) {
//登录失败:密码错误
model.addAttribute("msg", "密码错误");
return "login";
}
}
@RequestMapping("/toLogin")
public String toLogin(Model model){
return "login";
}
}
ShiroConfig类可以进行过滤
@Configuration
public class ShiroConfig {
/**
* 配置ShiroDialect,用于thymeleaf和shiro标签配合使用
* @return
*/
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(
@Qualifier("securityManager") DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
// 添加Shiro内置过滤器
/**
* Shiro内置过滤器,可以实现权限相关的拦截器
* 常用的过滤器
* anon::无需认证(登录)可以访问
* authc:必须认证才可以访问
* user:如果使用rememberMe的功能可以直接访问
* perms:该资源必须得到资源权限才可以访问
* role:该资源必须得到角色权限才可以访问
*/
Map filterMap = new LinkedHashMap();
filterMap.put("/testThymeleaf","anon");
filterMap.put("/login","anon");
// 过滤器的授权
filterMap.put("/add","perms[user:add]");
filterMap.put("/**","authc");
// 修改调整的登录页面
shiroFilterFactoryBean.setLoginUrl("/toLogin");
// 设置未授权提示页面
shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
return shiroFilterFactoryBean;
}
@Bean(name = "securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 关联realm
securityManager.setRealm(userRealm);
return securityManager;
}
@Bean(name = "userRealm")
public UserRealm getRealm(){
return new UserRealm();
}
}
UserRealm类继承AuthorizingRealm 并重写它的两个方法,一个是授权逻辑,一个是认证逻辑。
再将Servive业务层注入。
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserService userRealm;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行授权逻辑");
// 给资源进行授权
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 添加资源的授权字符串
info.addStringPermission("user:add");
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行认证逻辑");
// 填写Shiro判断逻辑 判断用户名和密码
// 1、判断用户名
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
User userByName = userRealm.getUserByName(token.getUsername());
if(userByName == null){
// 用户名不存在
return null; // Shiro底层会抛出UNKnowAccountException
}
// 2、判断密码
return new SimpleAuthenticationInfo("",userByName.getPassword(),"");
}
}
整个项目结构
login.html
登录页面
登录
test.html
thymeleaf
进入用户添加功能:用户添加
登录