Spring Boot & Shiro集成
导入 maven 依赖
导入spring-maven依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.5.2</version>
</dependency>
其他spring boot依赖包括
- mybatis相关,数据库相关
- thymeleaf相关
数据库表的建立
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`userId` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) NOT NULL,
`password` varchar(255) DEFAULT NULL,
PRIMARY KEY (`userId`),
UNIQUE KEY `user_username_uindex` (`username`),
UNIQUE KEY `user_password_uindex` (`password`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
简单的user表,后面功能可以再进行添加
编写获取账号密码的DAO层
我这里使用了mybatis,mybatis的配置我也放到这里了
mybatis配置
mybatis:
type-aliases-package: com.lucas.springbootlearn.pojo
mapper-locations: classpath:mappers/*.xml
druid配置
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
password: root
username: root
url: jdbc:mysql://localhost:3306/web?serverTimezone=UTC
dbType: mysql # 指定数据库类型 mysql
initialSize: 5 # 启动初始化连接数量
minIdle: 5 # 最小空闲连接
maxActive: 20 # 最大连接数量(包含使用中的和空闲的)
maxWait: 60000 # 最大连接等待时间 ,超出时间报错
type: com.alibaba.druid.pool.DruidDataSource
@Configuration
public class DruidConfig {
//注册bean
@Bean
@ConfigurationProperties(prefix = "spring.datasource")
public DataSource druidDataSource(){
return new DruidDataSource();
}
}
编写mapper
<mapper namespace="com.lucas.springbootlearn.dao.UserMapper">
<select id="findUserByName" resultType="user">
select * from user where username = '${username}'
</select>
</mapper>
编写DAO
@Mapper
@Repository
public interface UserMapper {
public User findUserByName(@Param("username") String username);
}
编写Shiro
编写Shiro配置类
shiro需要三样东西
- ShiroFilterFactoryBean
- DefaultWebSecurityManager
- Realm对象
@Configuration
public class ShiroConfig {
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean()
{
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//代码
return shiroFilterFactoryBean;
}
@Bean
public DefaultWebSecurityManager defaultWebSecurityManager()
{
DefaultWebSecurityManager defaultWebSecurityManager =
new DefaultWebSecurityManager();
//代码
return defaultWebSecurityManager;
}
@Bean
public Realm realm()
{
//代码
}
}
大致框架就是这样
编写realm
我们固然可以使用JdbcRealm
但是我们也可以编写自定义的Realm
我们新建一个Realm叫MyRealm
自定义的Realm集成AuthorizingRealm
毕竟方便
public class MyRealm extends AuthorizingRealm {
@Autowired
private UserMapper mapper;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//授权信息
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//登录验证的信息
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) authenticationToken;
//我们只需要根据用户名查询数据库
User user = mapper.findUserByName(
new String(usernamePasswordToken.getUsername())
);
//return null 会抛出一个用户名不存在异常
//我们这边只去实现登录的功能
if (user==null)
return null;
// shiro 执行比对密码的功能
return new SimpleAuthenticationInfo("",user.getPassword(),"");
}
}
授权部分暂时不写,只写了登录验证部分
- AuthenticationToken 是Controller层传入的Token
- 我们通过查询数据库获取正确的密码
- 我们判断是不是空的,如果为空进行处理
- 我们把比对密码的事情交给Shiro
简单的Controller编写
@RequestMapping("/userLogin")
public String login(User user, Model model, HttpSession session)
{
UsernamePasswordToken usernamePasswordToken =
new UsernamePasswordToken(user.getName(), user.getPassword());
Subject subject = SecurityUtils.getSubject();
try {
subject.login(usernamePasswordToken);
session.setAttribute("user",user);
}catch (UnknownAccountException uae) {
System.out.println("用户名不存在");
return "/login";
} catch (IncorrectCredentialsException ice) {
System.out.println("密码错误");
return "/login";
}
//权限管理这一部分还要继续添加
return "adminIndex";
}
稍微总结一下,Controller的编写分为这几步
- 获取表单提交的数据
- 封装成一个User对象
- 获取Subject
- 获取Token
- 验证登录/捕获异常
- 对异常进行处理
回到Config & 编写拦截器
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager)
{
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
// 添加过滤器
/*
* 1. anon: 无需认证就能访问
* 2. authc: 需要认证才能用
* 3. user: 记住我功能开启才能用
* 4. perms: 拥有权限才能访问
* 5. roles: 拥有某个角色权限才能访问
*/
HashMap<String, String> map = new LinkedHashMap<>();
//首页所有人可以访问
map.put("/allBook","anon");
map.put("/login","anon");
map.put("/","anon");
map.put("/logout","anon");
//其他管理员界面都要登录
map.put("/user/*","authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
shiroFilterFactoryBean.setLoginUrl("/login");
return shiroFilterFactoryBean;
}
@Bean(name = "defaultWebSecurityManager")
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("realm") MyRealm realm)
{
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
//关联 Realm
defaultWebSecurityManager.setRealm(realm);
return defaultWebSecurityManager;
}
@Bean(name = "realm")
public MyRealm realm()
{
return new MyRealm();
}
这里值得注意的是过滤器的配置
- anon: 无需认证就能访问
- authc: 需要认证才能用
- user: 记住我功能开启才能用
- perms: 拥有权限才能访问
- roles: 拥有某个角色权限才能访问
在这里还能配置更多的Shiro内容
用户权限的认证
在MyRealm中编写
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//授权信息
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
//这里给每个用户都添加了以下权限
simpleAuthorizationInfo.addStringPermission("admin.*");
return simpleAuthorizationInfo;
}
Config中编写权限要求
//其他管理员界面都要登录
map.put("/user/*","perms[admin:user]");
map.put("/book/*","perms[admin:book]");
map.put("/management/**","perms[admin:book]");
实际上在Realm中的权限添加是要去数据库中寻找的,这里比较方便
shiro整合thymeleaf
shiro整合thymeleaf也比较简单
增加以下依赖
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
在配置中要配置Bean
@Bean(name = "shiroDialect")
public ShiroDialect shiroDialect(){
return new ShiroDialect();
}
标签和jsp中的标签很像
参考