SpringBoot整合Shiro的代码详解
shiro是一个权限框架,具体的使用可以查看其官网http://shiro.apache.org/ 它提供了很方便的权限认证和登录的功能.
而springboot作为一个开源框架,必然提供了和shiro整合的功能!接下来就用springboot结合springmvc,mybatis,整合shiro完成对于用户登录的判定和权限的验证.
1.准备数据库表结构
这里主要涉及到五张表:用户表,角色表(用户所拥有的角色),权限表(角色所涉及到的权限),用户-角色表(用户和角色是多对多的),角色-权限表(角色和权限是多对多的).表结构建立的sql语句如下:
CREATETABLE`module`(
`mid`int(11)NOTNULLAUTO_INCREMENT,
`mname`varchar(255)DEFAULTNULL,
PRIMARYKEY(`mid`)
)ENGINE=InnoDBAUTO_INCREMENT=5DEFAULTCHARSET=utf8;
------------------------------
--Recordsofmodule
------------------------------
INSERTINTO`module`VALUES('1','add');
INSERTINTO`module`VALUES('2','delete');
INSERTINTO`module`VALUES('3','query');
INSERTINTO`module`VALUES('4','update');
------------------------------
--Tablestructureformodule_role
------------------------------
DROPTABLEIFEXISTS`module_role`;
CREATETABLE`module_role`(
`rid`int(11)DEFAULTNULL,
`mid`int(11)DEFAULTNULL,
KEY`rid`(`rid`),
KEY`mid`(`mid`),
CONSTRAINT`mid`FOREIGNKEY(`mid`)REFERENCES`module`(`mid`),
CONSTRAINT`rid`FOREIGNKEY(`rid`)REFERENCES`role`(`rid`)
)ENGINE=InnoDBDEFAULTCHARSET=utf8;
------------------------------
--Recordsofmodule_role
------------------------------
INSERTINTO`module_role`VALUES('1','1');
INSERTINTO`module_role`VALUES('1','2');
INSERTINTO`module_role`VALUES('1','3');
INSERTINTO`module_role`VALUES('1','4');
INSERTINTO`module_role`VALUES('2','1');
INSERTINTO`module_role`VALUES('2','3');
------------------------------
--Tablestructureforrole
------------------------------
DROPTABLEIFEXISTS`role`;
CREATETABLE`role`(
`rid`int(11)NOTNULLAUTO_INCREMENT,
`rname`varchar(255)DEFAULTNULL,
PRIMARYKEY(`rid`)
)ENGINE=InnoDBAUTO_INCREMENT=3DEFAULTCHARSET=utf8;
------------------------------
--Recordsofrole
------------------------------
INSERTINTO`role`VALUES('1','admin');
INSERTINTO`role`VALUES('2','customer');
------------------------------
--Tablestructureforuser
------------------------------
DROPTABLEIFEXISTS`user`;
CREATETABLE`user`(
`uid`int(11)NOTNULLAUTO_INCREMENT,
`username`varchar(255)DEFAULTNULL,
`password`varchar(255)DEFAULTNULL,
PRIMARYKEY(`uid`)
)ENGINE=InnoDBAUTO_INCREMENT=3DEFAULTCHARSET=utf8;
------------------------------
--Recordsofuser
------------------------------
INSERTINTO`user`VALUES('1','hlhdidi','123');
INSERTINTO`user`VALUES('2','xyycici','1992');
------------------------------
--Tablestructureforuser_role
------------------------------
DROPTABLEIFEXISTS`user_role`;
CREATETABLE`user_role`(
`uid`int(11)DEFAULTNULL,
`rid`int(11)DEFAULTNULL,
KEY`u_fk`(`uid`),
KEY`r_fk`(`rid`),
CONSTRAINT`r_fk`FOREIGNKEY(`rid`)REFERENCES`role`(`rid`),
CONSTRAINT`u_fk`FOREIGNKEY(`uid`)REFERENCES`user`(`uid`)
)ENGINE=InnoDBDEFAULTCHARSET=utf8;
------------------------------
--Recordsofuser_role
------------------------------
INSERTINTO`user_role`VALUES('1','1');
INSERTINTO`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=SELECT1FROMDUAL 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配置类
*@authorAdministrator
*
*/
@Configuration
publicclassDruidConfiguation{
@Bean
publicServletRegistrationBeanstatViewServle(){
ServletRegistrationBeanservletRegistrationBean=newServletRegistrationBean(newStatViewServlet(),"/druid/*");
//白名单:
servletRegistrationBean.addInitParameter("allow","192.168.1.218,127.0.0.1");
//IP黑名单(存在共同时,deny优先于allow):如果满足deny的即提示:Sorry,youarenotpermittedtoviewthispage.
servletRegistrationBean.addInitParameter("deny","192.168.1.100");
//登录查看信息的账号密码.
servletRegistrationBean.addInitParameter("loginUsername","druid");
servletRegistrationBean.addInitParameter("loginPassword","12345678");
//是否能够重置数据.
servletRegistrationBean.addInitParameter("resetEnable","false");
returnservletRegistrationBean;
}
@Bean
publicFilterRegistrationBeanstatFilter(){
FilterRegistrationBeanfilterRegistrationBean=newFilterRegistrationBean(newWebStatFilter());
//添加过滤规则.
filterRegistrationBean.addUrlPatterns("/*");
//添加不需要忽略的格式信息.
filterRegistrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
returnfilterRegistrationBean;
}
@Bean
PersistenceExceptionTranslationPostProcessorpersistenceExceptionTranslationPostProcessor(){
returnnewPersistenceExceptionTranslationPostProcessor();
}
//配置数据库的基本链接信息
@Bean(name="dataSource")
@Primary
@ConfigurationProperties(prefix="spring.datasource")//可以在application.properties中直接导入
publicDataSourcedataSource(){
returnDataSourceBuilder.create().type(com.alibaba.druid.pool.DruidDataSource.class).build();
}
@Bean
publicSqlSessionFactoryBeansqlSessionFactory(@Qualifier("dataSource")DataSourcedataSource){
SqlSessionFactoryBeanbean=newSqlSessionFactoryBean();
bean.setDataSource(dataSource);
returnbean;
}
}
接着在spring-boot-shiro-web下建立Application类:
@SpringBootApplication
@EnableTransactionManagement
@MapperScan("com.xyy.springboot.shiro.mapper")//配置mybatis包扫描
publicclassApplication{
publicstaticvoidmain(String[]args){
SpringApplication.run(Application.class,args);
}
}
紧接着,我们根据数据库的表结构在spring-boot-shiro-dao的项目下建立实体类User,Role,Module.它们的意义在上文中已经说明了:
接着就可以书写mapper了,注意,mapper所在的位置需要和Application类中配置的包扫描的位置保持一致,我们的需求是根据用户名在数据库中查询出指定的用户表的记录,与此同时查询出对应的角色以及角色所对应的权限,并且封装到实体类User中.UserMapper接口如下:
UserMapper.xml如下:
SELECTu.*,r.*,m.*FROMuseruinnerjoinuser_roleuronur.uid=u.uid innerjoinroleronr.rid=ur.rid innerjoinmodule_rolemronmr.rid=r.rid innerjoinmodulemonmr.mid=m.mid WHEREusername=#{username};
在spring-boot-shiro-service建立UserService和UserServiceImpl,完成业务层对于mapper的调用:
紧接着就是重点啦!我们需要在spring-boot-shiro-web工程下面建立两个类,这也是shiro中唯一需要程序员编写的两个类:类AuthRealm完成根据用户名去数据库的查询,并且将用户信息放入shiro中,供第二个类调用.CredentialsMatcher,完成对于密码的校验.其中用户的信息来自shiro.AuthRealm类如下:
publicclassAuthRealmextendsAuthorizingRealm{
@Autowired
privateUserServiceuserService;
//认证.登录
@Override
protectedAuthenticationInfodoGetAuthenticationInfo(AuthenticationTokentoken)throwsAuthenticationException{
UsernamePasswordTokenutoken=(UsernamePasswordToken)token;//获取用户输入的token
Stringusername=utoken.getUsername();
Useruser=userService.findUserByUserName(username);
returnnewSimpleAuthenticationInfo(user,user.getPassword(),this.getClass().getName());//放入shiro.调用CredentialsMatcher检验密码
}
//授权
@Override
protectedAuthorizationInfodoGetAuthorizationInfo(PrincipalCollectionprincipal){
Useruser=(User)principal.fromRealm(this.getClass().getName()).iterator().next();//获取session中的用户
Listpermissions=newArrayList<>();
Setroles=user.getRoles();
if(roles.size()>0){
for(Rolerole:roles){
Setmodules=role.getModules();
if(modules.size()>0){
for(Modulemodule:modules){
permissions.add(module.getMname());
}
}
}
}
SimpleAuthorizationInfoinfo=newSimpleAuthorizationInfo();
info.addStringPermissions(permissions);//将权限放入shiro中.
returninfo;
}
}
授权的方法是在碰到
publicclassCredentialsMatcherextendsSimpleCredentialsMatcher{
@Override
publicbooleandoCredentialsMatch(AuthenticationTokentoken,AuthenticationInfoinfo){
UsernamePasswordTokenutoken=(UsernamePasswordToken)token;
//获得用户输入的密码:(可以采用加盐(salt)的方式去检验)
StringinPassword=newString(utoken.getPassword());
//获得数据库中的密码
StringdbPassword=(String)info.getCredentials();
//进行密码的比对
returnthis.equals(inPassword,dbPassword);
}
}
接着就是shiro的配置类了,需要注意一点filterChainDefinitionMap必须是LinkedHashMap因为它必须保证有序:
shiro的配置类如下:
/**
*shiro的配置类
*@authorAdministrator
*
*/
@Configuration
publicclassShiroConfiguration{
@Bean(name="shiroFilter")
publicShiroFilterFactoryBeanshiroFilter(@Qualifier("securityManager")SecurityManagermanager){
ShiroFilterFactoryBeanbean=newShiroFilterFactoryBean();
bean.setSecurityManager(manager);
//配置登录的url和登录成功的url
bean.setLoginUrl("/login");
bean.setSuccessUrl("/home");
//配置访问权限
LinkedHashMapfilterChainDefinitionMap=newLinkedHashMap<>();
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);
returnbean;
}
//配置核心安全事务管理器
@Bean(name="securityManager")
publicSecurityManagersecurityManager(@Qualifier("authRealm")AuthRealmauthRealm){
System.err.println("--------------shiro已经加载----------------");
DefaultWebSecurityManagermanager=newDefaultWebSecurityManager();
manager.setRealm(authRealm);
returnmanager;
}
//配置自定义的权限登录器
@Bean(name="authRealm")
publicAuthRealmauthRealm(@Qualifier("credentialsMatcher")CredentialsMatchermatcher){
AuthRealmauthRealm=newAuthRealm();
authRealm.setCredentialsMatcher(matcher);
returnauthRealm;
}
//配置自定义的密码比较器
@Bean(name="credentialsMatcher")
publicCredentialsMatchercredentialsMatcher(){
returnnewCredentialsMatcher();
}
@Bean
publicLifecycleBeanPostProcessorlifecycleBeanPostProcessor(){
returnnewLifecycleBeanPostProcessor();
}
@Bean
publicDefaultAdvisorAutoProxyCreatordefaultAdvisorAutoProxyCreator(){
DefaultAdvisorAutoProxyCreatorcreator=newDefaultAdvisorAutoProxyCreator();
creator.setProxyTargetClass(true);
returncreator;
}
@Bean
publicAuthorizationAttributeSourceAdvisorauthorizationAttributeSourceAdvisor(@Qualifier("securityManager")SecurityManagermanager){
AuthorizationAttributeSourceAdvisoradvisor=newAuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(manager);
returnadvisor;
}
}
这样,shiro的配置就完成了!紧接着建立页面.login.jsp用于用户登录,index.jsp是用户主页,在没有登录的情况下是进不去的.内容分别如下:
index.jsp
欢迎${user.username}光临!请选择你的操作:
- 增加
- 删除
- 修改
- 查询
login.jsp
欢迎登录!${user.username}
OK,紧接着就是建立LoginController去测试结果了!这里需要注意,我们和shiro框架的交互完全通过Subject这个类去交互,用它完成登录,注销,获取当前的用户对象等操作:
@Controller
publicclassLoginController{
@RequestMapping("/login")
publicStringlogin(){
return"login";
}
@RequestMapping("/loginUser")
publicStringloginUser(Stringusername,Stringpassword,HttpSessionsession){
UsernamePasswordTokenusernamePasswordToken=newUsernamePasswordToken(username,password);
Subjectsubject=SecurityUtils.getSubject();
try{
subject.login(usernamePasswordToken);//完成登录
Useruser=(User)subject.getPrincipal();
session.setAttribute("user",user);
return"index";
}catch(Exceptione){
return"login";//返回登录页面
}
}
@RequestMapping("/logOut")
publicStringlogOut(HttpSessionsession){
Subjectsubject=SecurityUtils.getSubject();
subject.logout();
//session.removeAttribute("user");
return"login";
}
}
接下来就可以测试了,在没有登录的情况下,访问主页的时候会跳到登录的页面,而登录不同的用户也会随着用户所拥有的角色不同而显示不同的模块.
总结
以上所述是小编给大家介绍的SpringBoot整合Shiro的代码详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对毛票票网站的支持!