弄了一天的shiro,来总结一下springboot整合shiro
首先创建一个普通的springboot项目,配置上web和thymeleaf
项目的最终目录如下:
第一步:运行这个工程,看看能否正常运行
测试成功,开始正式搭建项目
1.首先导入依赖,方便起见,我把需要的依赖都放在这里了
org.springframework.boot spring-boot-starter-thymeleaf org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test com.github.theborakompanioni thymeleaf-extras-shiro 2.0.0 org.apache.shiro shiro-spring 1.7.1 mysql mysql-connector-java com.alibaba druid 1.1.12 org.mybatis.spring.boot mybatis-spring-boot-starter 2.2.0 log4j log4j 1.2.17 org.projectlombok lombok 1.18.22 provided
1.建包
resource目录下的mapper包我们需要在yml文件当中绑定它的位置,否则spring容器无法扫描到它
2.新建application.yml文件,这里方便起见,我把所有的配置都放在下面了,根据自己的情况来改就好
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&useSSL=false&characterEncoding=utf-8
driver-class-name: com.mysql.jdbc.Driver
type: com.alibaba.druid.pool.DruidDataSource
# springboot默认不注入一下的属性 需要自己绑定
#druid 数据源专有配置
#Spring Boot 默认是不注入这些属性值的,需要自己绑定
#druid 数据源专有配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECt 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
#配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
#如果允许时报错 java.lang.ClassNotFoundException: org.apache.log4j.Priority
#则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
mybatis:
# 包别名配置
type-aliases-package: com.fannnnn.pojo
# 扫描包
mapper-locations: classpath:mapper/*.xml
3.连接数据库,这里不多赘述
4.依次建实体类、mapper、*Mapper.xm、service、serviceImpl,这里我只写了一些后续要用到的方法,如果自己要去测的话可以加方法
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
private String pwd;
private String perms;
}
@Mapper
@Repository
public interface UserMapper {
//通过username获取用户
User getUserByName(String name);
//通过username获取用户权限
String getPerms(String username);
}
public interface UserService {
User getUserByName(String name);
String getPerms(String username);
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserMapper userMapper;
@Override
public User getUserByName(String name) {
return userMapper.getUserByName(name);
}
@Override
public String getPerms(String username) {
return userMapper.getPerms(username);
}
}
最后来测试,看看我们的方法能不能从数据库中拿出我们需要的数据
@SpringBootTest
class SpringbootShiro2ApplicationTests {
@Autowired
UserServiceImpl userService;
@Test
void contextLoads() {
System.out.println(userService.getUserByName("吴宇擘"));
System.out.println("他的权限是"+userService.getPerms("余德准"));
}
}
结果发现确实查出了我们想要的东西,好了,数据拿到了,那可以来做权限管理了
5.建一个index.html文件
Title
首页
编写路由
//可以对首页进行多个路径的设置
@RequestMapping({"/","index","index.html"})
public String index(){
return "index";
}
运行项目请求成功
6.接下来来多写几个页面以及修改原来的 index.html,为了之后的权限认证
index.html
Title
首页
add | delete
add.html和delete.html
Title
add
Title
delete
最后再来编写Controller层
@RequestMapping("/user/add")
public String add(){
return "user/add";
}
@RequestMapping("/user/delete")
public String delete(){
return "user/delete";
}
测试结果可以正常跳转
7.在config包当中开始配置shiro,shiro最重要的三个对象,subject(表示当前对象), securityManager(管理用户)以及realm
realm类我们单独拿出来管理
UserRealm.java
public class UserRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("执行了授权");
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了认证");
return null;
}
}
shiroConfig.java
@Configuration
public class shiroConfig {
//ShiroFilterFactoryBean
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(securityManager);
Map filterMap = new linkedHashMap<>();
//添加shiro的内置过滤器
//设置认证,表示必须认证才能访问以下两个请求
// filterMap.put("/user/add","authc");
// filterMap.put("/user/delete","authc");
//支持通配符
filterMap.put("/user/*","authc");
bean.setFilterChainDefinitionMap(filterMap);
return bean;
}
//securityManager
@Bean(name = "securityManager")
public DefaultWebSecurityManager defaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm);
return securityManager;
}
//realm
@Bean(name = "userRealm")
public UserRealm userRealm(){
return new UserRealm();
}
}
三个方法所返回的对象具有紧密的联系性,以上设置完毕后我们发现以及访问不了add和delete页面,说明请求成功了
我们来改进,当没有认证时,点击页面链接则直接跳转到登录页面,只要写好登录页,登录页的接口,最后将接口配置进shiroFilterFactoryBean中即可
Title
登录
@RequestMapping("/toLogin")
public String toLogin(){
return "login";
}
shiroConfig加入下面这段代码,表示无认证时,跳转到登录页面
//无认证时,跳转到登录页面
bean.setLoginUrl("/toLogin");
测试以后发现,在不登录的情况下确实会直接跳转到登录页面
接下来实现shiro的用户认证,用户认证我的理解就是是否登录
所以写好登录接口
@RequestMapping("/login")
public String login(String username, String password, Model model){
//获取当前的用户
Subject subject = SecurityUtils.getSubject();
//封装用户的登录数据
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
try {
subject.login(token);
return "index";
}catch (UnknownAccountException e){
model.addAttribute("msg","用户名错误");
return "login";
}catch (IncorrectCredentialsException e){
model.addAttribute("msg","密码错误");
return "login";
}
}
认证方法,取出数据库中的对象并判断
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了认证");
//拿到token
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
User user = userService.getUserByName(userToken.getUsername());
if(user==null){ //没有这个人
return null;
}
//自动验证密码
return new SimpleAuthenticationInfo("",user.getPwd(),"");
}
可以发现登录实现了拦截功能以及数据库中的用户才能够正常登录
接下来做授权实现
@RequestMapping("/noauth")
@ResponseBody
public String unauthorized(){
return "未经授权,无法访问此页面";
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了授权");
// SimpleAuthorizationInfo
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// info.addStringPermission("perms:add");
Subject subject = SecurityUtils.getSubject();
User user = (User) subject.getPrincipal(); //拿到user对象
info.addStringPermission(user.getPerms());
return info;
}
shiroFilterFactoryBean中添加授权
//设置授权,正常的情况下 没有授权会跳转到未授权页面 授权必须写在认证上面
filterMap.put("/user/add","perms[user:add]");
filterMap.put("/user/delete","perms[user:delete]");
设置未授权
//设置未授权
bean.setUnauthorizedUrl("/noauth");
最后一步来整合thymeleaf,因为不同权限的人应该只展示他们能看的内容,所以要在thymeleaf当中判断是否将某一内容展示给用户
修改index.html
Title
首页
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了认证");
//拿到token
UsernamePasswordToken userToken = (UsernamePasswordToken) token;
User user = userService.getUserByName(userToken.getUsername());
if(user==null){ //没有这个人
return null;
}
//设置session
Subject subject = SecurityUtils.getSubject();
Session session = subject.getSession();
session.setAttribute("loginUser",user);
//自动验证密码
return new SimpleAuthenticationInfo(user,user.getPwd(),"");
}
shiroConfig当中增加bean
@Bean
public ShiroDialect getShiroDialect(){
return new ShiroDialect();
}
``
最后测试,成功实现权限设置,不同权限的用户只能看不同的内容
最后再来实现注销功能
index.html中添加注销按钮
写好接口
@RequestMapping("/logout")
public String logout(){
Subject subject = SecurityUtils.getSubject();
subject.logout();
return "login";
}
测试成功!



