SpringBoot整合Shiro的代码详解

shiro是一个权限框架,具体的使用可以查看其官网 http://shiro.apache.org/  它提供了很方便的权限认证和登录的功能.

目前成都创新互联公司已为千余家的企业提供了网站建设、域名、雅安服务器托管网站托管运营、企业网站设计、高阳网站维护等服务,公司将坚持客户导向、应用为本的策略,正道将秉承"和谐、参与、激情"的文化,与客户和合作伙伴齐心协力一起成长,共同发展。

而springboot作为一个开源框架,必然提供了和shiro整合的功能!接下来就用springboot结合springmvc,mybatis,整合shiro完成对于用户登录的判定和权限的验证.

1.准备数据库表结构

这里主要涉及到五张表:用户表,角色表(用户所拥有的角色),权限表(角色所涉及到的权限),用户-角色表(用户和角色是多对多的),角色-权限表(角色和权限是多对多的).表结构建立的sql语句如下:

CREATE TABLE `module` (
 `mid` int(11) NOT NULL AUTO_INCREMENT,
 `mname` varchar(255) DEFAULT NULL,
 PRIMARY KEY (`mid`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of module
-- ----------------------------
INSERT INTO `module` VALUES ('1', 'add');
INSERT INTO `module` VALUES ('2', 'delete');
INSERT INTO `module` VALUES ('3', 'query');
INSERT INTO `module` VALUES ('4', 'update');
-- ----------------------------
-- Table structure for module_role
-- ----------------------------
DROP TABLE IF EXISTS `module_role`;
CREATE TABLE `module_role` (
 `rid` int(11) DEFAULT NULL,
 `mid` int(11) DEFAULT NULL,
 KEY `rid` (`rid`),
 KEY `mid` (`mid`),
 CONSTRAINT `mid` FOREIGN KEY (`mid`) REFERENCES `module` (`mid`),
 CONSTRAINT `rid` FOREIGN KEY (`rid`) REFERENCES `role` (`rid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of module_role
-- ----------------------------
INSERT INTO `module_role` VALUES ('1', '1');
INSERT INTO `module_role` VALUES ('1', '2');
INSERT INTO `module_role` VALUES ('1', '3');
INSERT INTO `module_role` VALUES ('1', '4');
INSERT INTO `module_role` VALUES ('2', '1');
INSERT INTO `module_role` VALUES ('2', '3');
-- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
 `rid` int(11) NOT NULL AUTO_INCREMENT,
 `rname` varchar(255) DEFAULT NULL,
 PRIMARY KEY (`rid`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES ('1', 'admin');
INSERT INTO `role` VALUES ('2', 'customer');
-- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
 `uid` int(11) NOT NULL AUTO_INCREMENT,
 `username` varchar(255) DEFAULT NULL,
 `password` varchar(255) DEFAULT NULL,
 PRIMARY KEY (`uid`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('1', 'hlhdidi', '123');
INSERT INTO `user` VALUES ('2', 'xyycici', '1992');
-- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
 `uid` int(11) DEFAULT NULL,
 `rid` int(11) DEFAULT NULL,
 KEY `u_fk` (`uid`),
 KEY `r_fk` (`rid`),
 CONSTRAINT `r_fk` FOREIGN KEY (`rid`) REFERENCES `role` (`rid`),
 CONSTRAINT `u_fk` FOREIGN KEY (`uid`) REFERENCES `user` (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES ('1', '1');
INSERT INTO `user_role` VALUES ('2', '2');

 2.建立Maven工程,建立实体类,搭建mybatis开发环境

maven工程的基本目录如下:

为了方便,直接在父工程中,导入全部的依赖:


 
    org.springframework.boot
    spring-boot-starter-parent
    1.4.0.RELEASE
 
 
      1.7
    UTF-8
 
 
    
      org.springframework.boot
      spring-boot-starter-web
    
    
        org.springframework.boot
        spring-boot-devtools
        true
        true
    
    
      org.springframework.boot
      spring-boot-starter-test
      test
    
   
    MySQL
    mysql-connector-java
    runtime
  
      
      com.alibaba
      druid
      1.0.18
    
    
      org.apache.commons
      commons-lang3
      3.4
    
    
       org.apache.commons
       commons-pool2
    
  
  org.mybatis.spring.boot
  mybatis-spring-boot-starter
  1.1.1

  
      com.alibaba
      druid
      1.0.18
    
    
      org.apache.commons
      commons-lang3
      3.4
    
    
       org.apache.commons
       commons-pool2
    
    
     
      org.apache.shiro 
      shiro-core 
      1.2.2 
     
    
      org.apache.shiro
      shiro-spring
      1.2.2
    
    
    
      org.apache.shiro
      shiro-ehcache
      1.2.2
    
    
    
     org.springframework
     spring-context-support
    
    
      
        javax.servlet
        javax.servlet-api
        provided
      
      
        javax.servlet
        jstl
      
    
      
        org.springframework.boot
        spring-boot-starter-tomcat
        provided
      
      
        org.apache.tomcat.embed
        tomcat-embed-jasper
        provided
      
  
 
    
      
      org.springframework.boot
      spring-boot-maven-plugin
      
        
          
            repackage
          
        
      
      
         
        true
      
      
    
  
 
   spring-boot-shiro-dao
   spring-boot-shiro-service
   spring-boot-shiro-web
 

可以看出这里采用的是阿里巴巴的Druid数据库.在spring-boot-shiro-web下建立application.properties文件.它主要配置对于数据库信息和jsp的支持:

##tomcat##
server.tomcat.uri-encoding=UTF-8
##Druid##
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?characterEncoding=UTF-8
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.initialSize=5
spring.datasource.minIdle=5
spring.datasource.maxActive=20
spring.datasource.maxWait=60000
spring.datasource.timeBetweenEvictionRunsMillis=60000
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=SELECT 1 FROM DUAL
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
spring.datasource.poolPreparedStatements=true
spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
spring.datasource.filters=stat,wall,log4j
spring.datasource.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
spring.datasource.useGlobalDataSourceStat=true
##jsp##
spring.mvc.view.prefix=/jsp/
spring.mvc.view.suffix=.jsp

在spring-boot-shiro-web下建立数据库连接池的配置类完成对于数据库连接池的配置:

/**
 * 数据库连接池&Mybatis配置类
 * @author Administrator
 *
 */
@Configuration
public class DruidConfiguation {
  @Bean
  public ServletRegistrationBean statViewServle(){
    ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(),"/druid/*");
    //白名单:
    servletRegistrationBean.addInitParameter("allow","192.168.1.218,127.0.0.1");
    //IP黑名单 (存在共同时,deny优先于allow) : 如果满足deny的即提示:Sorry, you are not permitted to view this page.
    servletRegistrationBean.addInitParameter("deny","192.168.1.100");
    //登录查看信息的账号密码.
    servletRegistrationBean.addInitParameter("loginUsername","druid");
    servletRegistrationBean.addInitParameter("loginPassword","12345678");
    //是否能够重置数据.
    servletRegistrationBean.addInitParameter("resetEnable","false");
    return servletRegistrationBean;
  }
  @Bean
  public FilterRegistrationBean statFilter(){
    FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
    //添加过滤规则.
    filterRegistrationBean.addUrlPatterns("/*");
    //添加不需要忽略的格式信息.
    filterRegistrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
    return filterRegistrationBean;
    }
  @Bean
  PersistenceExceptionTranslationPostProcessor persistenceExceptionTranslationPostProcessor(){
    return new PersistenceExceptionTranslationPostProcessor();
  }
  //配置数据库的基本链接信息
  @Bean(name = "dataSource")
   @Primary
   @ConfigurationProperties(prefix = "spring.datasource")  //可以在application.properties中直接导入
   public DataSource dataSource(){
     return DataSourceBuilder.create().type(com.alibaba.druid.pool.DruidDataSource.class).build();
   }
  @Bean
   public SqlSessionFactoryBean sqlSessionFactory(@Qualifier("dataSource") DataSource dataSource) {
     SqlSessionFactoryBean bean=new SqlSessionFactoryBean();
     bean.setDataSource(dataSource);
     return bean;
   }
}

接着在spring-boot-shiro-web下建立Application类:

@SpringBootApplication
@EnableTransactionManagement
@MapperScan("com.xyy.springboot.shiro.mapper")//配置mybatis包扫描
public class Application {
  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }
}

紧接着,我们根据数据库的表结构在spring-boot-shiro-dao的项目下建立实体类User,Role,Module.它们的意义在上文中已经说明了:

接着就可以书写mapper了,注意,mapper所在的位置需要和Application类中配置的包扫描的位置保持一致,我们的需求是根据用户名在数据库中查询出指定的用户表的记录,与此同时查询出对应的角色以及角色所对应的权限,并且封装到实体类User中.UserMapper接口如下:

UserMapper.xml如下:


  
    
    
    
    
      
      
      
        
        
      
    
  
  

在spring-boot-shiro-service建立UserService和UserServiceImpl,完成业务层对于mapper的调用:

紧接着就是重点啦!我们需要在spring-boot-shiro-web工程下面建立两个类,这也是shiro中唯一需要程序员编写的两个类:类AuthRealm完成根据用户名去数据库的查询,并且将用户信息放入shiro中,供第二个类调用.CredentialsMatcher,完成对于密码的校验.其中用户的信息来自shiro.AuthRealm类如下:

public class AuthRealm extends AuthorizingRealm{
  @Autowired
  private UserService userService;
  //认证.登录
  @Override
  protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    UsernamePasswordToken utoken=(UsernamePasswordToken) token;//获取用户输入的token
    String username = utoken.getUsername();
    User user = userService.findUserByUserName(username);
    return new SimpleAuthenticationInfo(user, user.getPassword(),this.getClass().getName());//放入shiro.调用CredentialsMatcher检验密码
  }
  //授权
  @Override
  protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
    User user=(User) principal.fromRealm(this.getClass().getName()).iterator().next();//获取session中的用户
    List permissions=new ArrayList<>();
    Set roles = user.getRoles();
    if(roles.size()>0) {
      for(Role role : roles) {
        Set modules = role.getModules();
        if(modules.size()>0) {
          for(Module module : modules) {
            permissions.add(module.getMname());
          }
        }
      }
    }
    SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
    info.addStringPermissions(permissions);//将权限放入shiro中.
    return info;
  }
}

授权的方法是在碰到标签的时候调用的,它会去检测shiro框架中的权限(这里的permissions)是否包含有该标签的name值,如果有,里面的内容显示,如果没有,里面的内容不予显示(这就完成了对于权限的认证.)下面是CredentialsMatcher:

public class CredentialsMatcher extends SimpleCredentialsMatcher{
  @Override
  public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
    UsernamePasswordToken utoken=(UsernamePasswordToken) token;
    //获得用户输入的密码:(可以采用加盐(salt)的方式去检验)
    String inPassword = new String(utoken.getPassword());
    //获得数据库中的密码
    String dbPassword=(String) info.getCredentials();
    //进行密码的比对
    return this.equals(inPassword, dbPassword);
  }
}

接着就是shiro的配置类了,需要注意一点filterChainDefinitionMap必须是LinkedHashMap因为它必须保证有序:

     shiro的配置类如下:

/**
 * shiro的配置类
 * @author Administrator
 *
 */
@Configuration
public class ShiroConfiguration {
  @Bean(name="shiroFilter")
  public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager manager) {
    ShiroFilterFactoryBean bean=new ShiroFilterFactoryBean();
    bean.setSecurityManager(manager);
    //配置登录的url和登录成功的url
    bean.setLoginUrl("/login");
    bean.setSuccessUrl("/home");
    //配置访问权限
    LinkedHashMap filterChainDefinitionMap=new LinkedHashMap<>();
    filterChainDefinitionMap.put("/jsp/login.jsp*", "anon"); //表示可以匿名访问
    filterChainDefinitionMap.put("/loginUser", "anon"); 
    filterChainDefinitionMap.put("/logout*","anon");
    filterChainDefinitionMap.put("/jsp/error.jsp*","anon");
    filterChainDefinitionMap.put("/jsp/index.jsp*","authc");
    filterChainDefinitionMap.put("/*", "authc");//表示需要认证才可以访问
    filterChainDefinitionMap.put("/**", "authc");//表示需要认证才可以访问
    filterChainDefinitionMap.put("/*.*", "authc");
    bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
    return bean;
  }
  //配置核心安全事务管理器
  @Bean(name="securityManager")
  public SecurityManager securityManager(@Qualifier("authRealm") AuthRealm authRealm) {
    System.err.println("--------------shiro已经加载----------------");
    DefaultWebSecurityManager manager=new DefaultWebSecurityManager();
    manager.setRealm(authRealm);
    return manager;
  }
  //配置自定义的权限登录器
  @Bean(name="authRealm")
  public AuthRealm authRealm(@Qualifier("credentialsMatcher") CredentialsMatcher matcher) {
    AuthRealm authRealm=new AuthRealm();
    authRealm.setCredentialsMatcher(matcher);
    return authRealm;
  }
  //配置自定义的密码比较器
  @Bean(name="credentialsMatcher")
  public CredentialsMatcher credentialsMatcher() {
    return new CredentialsMatcher();
  }
  @Bean
  public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
    return new LifecycleBeanPostProcessor();
  }
  @Bean
  public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
    DefaultAdvisorAutoProxyCreator creator=new DefaultAdvisorAutoProxyCreator();
    creator.setProxyTargetClass(true);
    return creator;
  }
  @Bean
  public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager manager) {
    AuthorizationAttributeSourceAdvisor advisor=new AuthorizationAttributeSourceAdvisor();
    advisor.setSecurityManager(manager);
    return advisor;
  }
}

这样,shiro的配置就完成了!紧接着建立页面.login.jsp用于用户登录,index.jsp是用户主页,在没有登录的情况下是进不去的.内容分别如下:

index.jsp

欢迎${user.username }光临!请选择你的操作:


  • 增加
  • 删除
  • 修改
  • 查询
点我注销

login.jsp

欢迎登录!${user.username }



OK,紧接着就是建立LoginController去测试结果了!这里需要注意,我们和shiro框架的交互完全通过Subject这个类去交互,用它完成登录,注销,获取当前的用户对象等操作:

@Controller
public class LoginController {
  @RequestMapping("/login")
  public String login() {
    return "login";
  }
  @RequestMapping("/loginUser")
  public String loginUser(String username,String password,HttpSession session) {
    UsernamePasswordToken usernamePasswordToken=new UsernamePasswordToken(username,password);
    Subject subject = SecurityUtils.getSubject();
    try {
      subject.login(usernamePasswordToken);  //完成登录
      User user=(User) subject.getPrincipal();
      session.setAttribute("user", user);
      return "index";
    } catch(Exception e) {
      return "login";//返回登录页面
    }
  }
  @RequestMapping("/logOut")
  public String logOut(HttpSession session) {
    Subject subject = SecurityUtils.getSubject();
    subject.logout();
//    session.removeAttribute("user");
    return "login";
  }
}

接下来就可以测试了,在没有登录的情况下,访问主页的时候会跳到登录的页面,而登录不同的用户也会随着用户所拥有的角色不同而显示不同的模块.

总结

以上所述是小编给大家介绍的SpringBoot整合Shiro的代码详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对创新互联网站的支持!


文章标题:SpringBoot整合Shiro的代码详解
转载注明:http://myzitong.com/article/psoiis.html