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工程的基本目錄如下:
為了方便,直接在父工程中,導入全部的依賴:
<!-- springboot的啟動所需配置.包括自動配置,封裝jar包等等 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.0.RELEASE</version>
</parent>
<properties>
<java.version>1.7</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
<scope>true</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.18</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.1.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.18</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- shiro spring. -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.2.2</version>
</dependency>
<!-- shiro ehcache -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.2.2</version>
</dependency>
<!--
包含支持UI模版(Velocity,FreeMarker,JasperReports),
郵件服務,
腳本服務(JRuby),
緩存Cache(EHCache),
任務計劃Scheduling(uartz)。
-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<!-- servlet 依賴. -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<!-- tomcat 的支持.-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
<configuration>
<!--fork : 如果沒有該項配置則devtools不會起作用,即應用不會restart -->
<fork>true</fork>
</configuration>
</plugin>
</plugins>
</build>
<modules>
<module>spring-boot-shiro-dao</module>
<module>spring-boot-shiro-service</module>
<module>spring-boot-shiro-web</module>
</modules>
可以看出這里采用的是阿里巴巴的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如下:
<mapper namespace="com.xyy.springboot.shiro.mapper.UserMapper">
<resultMap type="com.xyy.springboot.shiro.pojo.User" id="userMap">
<id property="uid" column="uid"/>
<result property="username" column="username"/>
<result property="password" column="password"/>
<collection property="roles" ofType="com.xyy.springboot.shiro.pojo.Role">
<id property="rid" column="rid"/>
<result property="rname" column="rname"/>
<collection property="modules" ofType="com.xyy.springboot.shiro.pojo.Module">
<id property="mid" column="mid"/>
<result property="mname" column="mname"/>
</collection>
</collection>
</resultMap>
<select id="findByUserName" parameterType="string" resultMap="userMap">
SELECT u.*,r.*,m.* FROM user u inner join user_role ur on ur.uid=u.uid
inner join role r on r.rid=ur.rid
inner join module_role mr on mr.rid=r.rid
inner join module m on mr.mid=m.mid
WHERE username=#{username};
</select>
</mapper>
在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<String> permissions=new ArrayList<>();
Set<Role> roles = user.getRoles();
if(roles.size()>0) {
for(Role role : roles) {
Set<Module> 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:hasPermission>標簽的時候調用的,它會去檢測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<String, String> 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
<h2>歡迎${user.username }光臨!請選擇你的操作:</h2><br>
<ul>
<shiro:hasPermission name="add"><li>增加</li></shiro:hasPermission>
<shiro:hasPermission name="delete"><li>刪除</li></shiro:hasPermission>
<shiro:hasPermission name="update"><li>修改</li></shiro:hasPermission>
<shiro:hasPermission name="query"><li>查詢</li></shiro:hasPermission>
</ul>
<a href="${pageContext.request.contextPath }/logOut" rel="external nofollow" >點我注銷</a>
login.jsp
<h2>歡迎登錄!${user.username }</h2>
<form action="${pageContext.request.contextPath }/loginUser" method="post">
<input type="text" name="username"><br>
<input type="password" name="password"><br>
<input type="submit" value="提交">
</form>
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的代碼詳解,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對億速云網站的支持!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。