springboot中shiro 的应用
apache shiro 是一个简单便捷的安全框架,也是官方推荐的安全框架,作用和spring scurity一样,但是却比scurity 轻便,灵活,由于spring boot 中没有整合shiro 所以如果我们要在项目中使用shiro需要自己配置一些东西 接下来开始介绍shiro 的使用
千里之行始于足下,先来认识shiro,上面这张图就是shiro官网的介绍图,可以很清楚的看出来 shiro基本功能包括四个方面
1. authentication(权限认证):通常用于用户登录
2. Autoorization(授权):主要就是给角色授权,就是说这个用户能做什么不能做什么
3. Session管理器:知道如何创建session或者如何管理session
4. Cryptography(加密):通过加密算法,对数据加密的同时,保证数据容易使用
当然也支持其他的一些功能(日后再聊)比方说支持缓存,记住密码,web支持,并发,支持单元测试,支持以另一个用户运行(在许可的情况下)
当然咯 shiro,他不会自己维护权限,这里需要我们去设计。接下来认识shiro代码
当我们的程序开始运行的时候,
1#首先会给到 subject,也就是当前应用程序的用户,是一个抽象的概念
2# SecurityManager 一个安全管理器,也是shiro中真正的执行者,管理着subject
3# realm 域 也就是安全的数据源,最简单的应用就是数据库
接下来看看shiro的架构图,当我们把这张图搞懂了,那么shiro 的学习就只剩下了解代码了
这张图不多介绍,基本的上面也都讲过,需要注意的一点就是,我们可以看得出基本这些shiro中的主体都是被security Manager管理着,
这里使用的是maven,贴上使用的dependency
[code] <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-core</artifactId> <version>1.4.0</version> </dependency> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.4.0</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.10</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.7</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.22</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>4.3.13.RELEASE</version> </dependency> <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency>
启动方式是用的maven的插件启动的,启动的时候要用maven中的springboot启动,如果直接点击运行,程序是无法找到jsp文件,而报404错误。
[code] <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build>
接下来看代码了,数据库连接使用的是mybaits,所以实体类,mapper文件(放在resource下面),dao接口都是通过mybatis generator自动生成的,就不贴代码 了,不懂mybais逆向工程的请戳 mybaits逆向工程,直接贴一张图,这张图也是这个demo的所有java代码了
接下来就是application.properties配置文件中的内容
[code] #配置数据库连接 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 #配置mabatis位置 mybatis.mapepr-location=mybatis/*.xml mybatis.type-aliases-package=com.lj.shiromanagerweb.model mybatis.config-location=classpath:mybatis-config.xml #配置springmvc spring.mvc.view.prefix=/pages/ spring.mvc.view.suffix=.jsp
服务端只写了一个方法,
[code]import com.lj.shiromanagerweb.dao.UserMapper; import com.lj.shiromanagerweb.model.User; import org.springframework.stereotype.Service; import javax.annotation.Resource; /** * @author Eric * @date create in2018/8/7 21:06 */ @Service public class UserService { @Resource UserMapper userMapper; public User findByUserName(String username) { return userMapper.findByUsername(username); } }
需要说明的一点是因为这个地方查询数据库是夺标联查,所以,在mapper文件的这个方法稍稍有些不一样mapper中的findByuserName代码如下
[code]<select id="findByUsername" parameterType="string" resultMap="userMap"> SELECT u.*, r.*, p.* FROM user u INNER JOIN user_role ur on ur.uid = u.uid INNER JOIN role r on r.rid = ur.rid INNER JOIN permission_role pr on pr.rid = r.rid INNER JOIN permission p on pr.pid = p.pid WHERE u.username = #{username} </select>
重点是接下来的三个类中的代码
1.自定义realm文件 AuthRealm.java 主要做两件事,一件事就是从数据库查询出相关数据,来验证用户,第二件事就是获取相关权限,给用户授权
[code]import com.lj.shiromanagerweb.model.Permission; import com.lj.shiromanagerweb.model.Role; import com.lj.shiromanagerweb.model.User; import com.lj.shiromanagerweb.service.UserService; import org.apache.commons.collections.CollectionUtils; import org.apache.shiro.authc.*; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.springframework.beans.factory.annotation.Autowired; import java.util.*; /** * 自定义realm授权部分 * * @author Eric * @date create in2018/8/7 21:30 */ public class AuthRealm extends AuthorizingRealm { @Autowired private UserService userService; /** * 授权方式 * @param principals * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { //通过传进来的principals迭代获取用户 User user = (User) principals.fromRealm(this.getClass().getName()).iterator().next(); // String userName = (String) principals.getPrimaryPrincipal(); List<String> permissionList = new ArrayList<>();// List<String> roleNameList = new ArrayList<>(); //获取登录人的角色,通过相关角色查询权限 Set<Role> roleSet = user.getRoles(); if (CollectionUtils.isNotEmpty(roleSet)) { for (Role role : roleSet) { roleNameList.add(role.getRname()); Set<Permission> permissionSet = role.getPermissions(); if (CollectionUtils.isNotEmpty(permissionSet)) { for (Permission permission : permissionSet) { permissionList.add(permission.getPname()); } } } } SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.addStringPermissions(permissionList); info.addRoles(roleNameList); return info; } /** * 认证 * @param token * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token; String username = usernamePasswordToken.getUsername(); User user = userService.findByUserName(username); return new SimpleAuthenticationInfo(user, user.getPassword(), this.getClass().getName()); } }
因为在shiro底层校验密码是通过SimpleCredentialsMatcher,那么这里我们也是实现了SimpleCredentialsMatcher,让校验的方式自己来实现,代码如下:
[code]/** * 告诉我们密码的校验规则,密码校验规则由我们自己实现 * @author Eric * @date create in2018/8/7 22:15 */ public class CredentialMatcher extends SimpleCredentialsMatcher{ @Override public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token; String password = new String(usernamePasswordToken.getPassword()); String dbPassword = (String) info.getCredentials(); return this.equals(password, dbPassword); } }
接下来就需要写我们shiro中的一些配置,我们在ShiroConfiguration这个类里面注入,这样一来,shiro的校验读取都是按照我们写的方式来操作
[code]import org.apache.shiro.mgt.SecurityManager; import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor; import org.apache.shiro.spring.web.ShiroFilterFactoryBean; import org.apache.shiro.web.mgt.DefaultWebSecurityManager; import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; import java.util.LinkedList; /** * * 定制springboot与shiro的关系,让系统在刚开始的时候就读取@Configuration * * 当程序开始的时候先读取shiroFilter--->securityManager---authRealm--->credentialMatcher * @author Eric * @date create in2018/8/7 22:21 */ @Configuration public class ShiroConfiguration { /** * anon:匿名访问 * authc;form表单验证 * authBasic:基础的http验证 * logout:注销拦截器 * noSessionCreation:没有session创建session的拦截器 * perms:权限县验证 * roles:角色验证 * @param manager * @return */ @Bean("shiroFilter") public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager manager) { ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean(); bean.setSecurityManager(manager); //登录页面 bean.setLoginUrl("/login"); //登录成功之后跳转的页面 bean.setSuccessUrl("/index"); //权限认证失败的时候跳转的页面 bean.setUnauthorizedUrl("/unauthorized"); LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>(); //前面的url使用后面的过滤器 /* filterChainDefinitionMap.put("/index", "authc"); filterChainDefinitionMap.put("/login", "anon"); filterChainDefinitionMap.put("loginUser", "anon"); filterChainDefinitionMap.put("/**", "user");*/ bean.setFilterChainDefinitionMap(filterChainDefinitionMap); return bean; } /** * 使用我们自己的方式去验证一个用户 * @param authRealm * @return */ @Bean("securityManager") public SecurityManager securityManager(@Qualifier("authRealm") AuthRealm authRealm) { DefaultWebSecurityManager manager = new DefaultWebSecurityManager(); manager.setRealm(authRealm); return manager; } /** * 使用自定义realm 和自定义的密码校验规则 * @param matcher * @return */ @Bean("authRealm") public AuthRealm authRealm(@Qualifier("credentialMatcher") CredentialMatcher matcher) { AuthRealm authRealm = new AuthRealm(); authRealm.setCredentialsMatcher(matcher); return authRealm; } /** * 校验密码的规则,这里是由自己定义的 * @return */ @Bean("credentialMatcher") public CredentialMatcher credentialMatcher() { return new CredentialMatcher(); } /** * 让程序使用自定义的securityManager * @return */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor(); advisor.setSecurityManager(securityManager); return advisor; } @Bean public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator(); //默认值是false creator.setProxyTargetClass(true); return creator; } }
接下来及时springboot 的main方法 这个方法里面有一些需要注意的,及时天剑@MapperScan,这是mybatis中,让配置文件和dao联系在一起
[code]@SpringBootApplication @ComponentScan @MapperScan(basePackages = {"com.lj.shiromanagerweb.dao"}) public class ShiroManagerWebApplication { public static void main(String[] args) { SpringApplication.run(ShiroManagerWebApplication.class, args); } }
准备工作都已经做好了,那么接下来只剩下前段代码,和controller了,前段代码就省掉了
TestController.java 代码如下
[code]import com.lj.shiromanagerweb.model.User; import lombok.extern.slf4j.Slf4j; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.subject.Subject; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpSession; /** * @author Eric * @date create in2018/8/7 22:45 */ @Controller @Slf4j public class TestController { @RequestMapping("/login") public String login() { return "login"; } @RequestMapping("/logout") public String logout() { Subject subject = SecurityUtils.getSubject(); if (subject!=null) { subject.logout(); } return "logout"; } @RequestMapping("/index") public String index() { return "index"; } @RequestMapping("/loginUser") public String loginUser(@RequestParam("username") String username, @RequestParam("password") String password, HttpSession session, Model model) { System.out.println("username = " + username); UsernamePasswordToken token = new UsernamePasswordToken(username, password); Subject subject = SecurityUtils.getSubject(); String username1 = new String(token.getUsername()); String password1 = new String(token.getPassword()); log.info("username1={};password1={}",username1,password1); try { subject.login(token); User user = (User) subject.getPrincipal(); session.setAttribute("user", user); return "index"; } catch (AuthenticationException e) { e.printStackTrace(); model.addAttribute("msg", "用户名或者账号错误"); return "login"; } } }
这样就大功告成了,,用maven中的springboot插件启动。
阅读更多
- springboot整合shiro应用
- spring boot整合redis,实现shiro的CacheManager
- Spring Boot中Web应用的统一异常处理
- Spring boot应用启动原理分析
- IX. Spring Boot应用
- 《spring-boot学习》-05-spring boot中redis应用
- springboot学习笔记-5 springboot整合shiro
- Spring Boot + Spring Cloud 实现权限管理系统 后端篇(十一):集成 Shiro 框架
- Spring Boot常见应用属性默认值
- Spring boot(4)-应用打包部署
- Apache Shiro权限框架在SpringMVC+Hibernate中的应用
- SpringBoot整合Shiro的代码详解
- 使用 Spring Boot 快速构建 Spring 框架应用
- SpringBoot之将jar应用转化为war应用
- [置顶] spring-boot实战:shiro
- Spring Boot应用之数据加密以及字段过滤
- [spring-boot] 集成shiro
- Spring boot+Shiro+ spring MVC+swagger UI +Mybatis+mysql+Vue +Element UI 之四 vue 基本知识点概述
- springboot集成jetcache及应用
- Spring Boot 构建应用——整合消息中间件 Kafka