springboot整合shiro

      http://www.cnblogs.com/hlhdidi/p/6376457.htmlcss

shiro是一個權限框架,具體的使用能夠查看其官網 http://shiro.apache.org/  它提供了很方便的權限認證和登陸的功能.html

  而springboot做爲一個開源框架,必然提供了和shiro整合的功能!接下來就用springboot結合springmvc,mybatis,整合shiro完成對於用戶登陸的斷定和權限的驗證.java

  1.準備數據庫表結構mysql

  這裏主要涉及到五張表:用戶表,角色表(用戶所擁有的角色),權限表(角色所涉及到的權限),用戶-角色表(用戶和角色是多對多的),角色-權限表(角色和權限是多對多的).表結構創建的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開發環境web

  maven工程的基本目錄以下:spring

  爲了方便,直接在父工程中,導入所有的依賴:sql

複製代碼
 <!-- 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下創建數據庫鏈接池的配置類完成對於數據庫鏈接池的配置:apache

複製代碼
/** * 數據庫鏈接池&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

複製代碼
<h1>歡迎${user.username }光臨!請選擇你的操做:</h1><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">點我註銷</a>
複製代碼

  login.jsp

複製代碼
<h1>歡迎登陸!${user.username }</h1> <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"; } }
複製代碼

  接下來就能夠測試了,在沒有登陸的狀況下,訪問主頁的時候會跳到登陸的頁面,而登陸不一樣的用戶也會隨着用戶所擁有的角色不一樣而顯示不一樣的模塊.

相關文章
相關標籤/搜索