文章目录
开篇必读:一、开发环境二、项目搭建三、配置 shiro 配置类1、自定义 Realm 类。2、创建shiro配置类。四、创建页面、Controller、 Service等。五、修改自定义 MyRealm六、配置资源访问权限 ShiroConfig开篇必读:
shiro这个框架虽然学习难度不高,但是底层封装很深,一篇博客也讲不明白,这东西估计得再写几篇文章,这篇文章主要是搭建一下shiro的项目架构,有shiro的基础功能。后期要添加的东西也不少,以这篇博客搭建的项目为基础,要想学shiro得一段时间,不是说你懂了就是你会了,你得动手写代码。键盘敲不烂,薪资不过万。后面继续更新,全部看完咱们不敢保证你一定会学会shiro,但一定会有收获。
坚持!
一、开发环境
二、项目搭建
1、创建一个SpringBoot项目导入相关pom依赖:
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.2.RELEASE</version><relativePath/><!-- lookup parent from repository --></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId><version>2.1.3.RELEASE</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><version>2.1.3.RELEASE</version><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId><version>2.1.3.RELEASE</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><scope>test</scope><version>4.12</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency><!--连接池--><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.16</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>1.4.0</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.1.0</version></dependency><dependency><groupId>com.github.theborakompanioni</groupId><artifactId>thymeleaf-extras-shiro</artifactId><version>2.0.0</version></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId><configuration><fork>true</fork><addResources>true</addResources></configuration></plugin></plugins></build></project>
2、在 resources 中创建 application.yml 配置文件。
详细配置如下:
如果你的idea是老版本可能因为系统找不到MyBatis的mapper目录而报错,你可以在resource 目录下创建mapper文件夹,也可以将 yml 配置文件中与MyBatis相关的配置暂时关掉 (注释掉的意思,配置文件中注释使用#号)。
server:port: 8080spring:application:name: shiro-springbootdatasource:url: jdbc:mysql://localhost:3306/hibernateusername: rootpassword: 123456driver-class-name: com.mysql.jdbc.Drivertype: com.alibaba.druid.pool.xa.DruidXADataSourcethymeleaf:cache: falsemvc:static-path-pattern: /templates/user/**mybatis:mapper-locations: classpath:mapper/*.xmltype-aliases-package: com.zkr.mingyu.entityconfiguration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpllogging:level:com.example.demo.dao : debug
SpringBoot开启热部署方法如下:见我另一篇博客介绍。
飞机票:SpringBoot 项目如何开启热部署
3、创建 SpringBoot 启动器 Application.class:
启动类源码:
package com.zkr.mingyu;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplicationpublic class Application {public static void main(String[] args) {SpringApplication.run(Application.class,args);}}
测试项目是否可以正常启动,访问 localhost:8080 端口,查看是否运行正常。
三、配置 shiro 配置类
目前的项目目录:
编写配置类与 SpringBoot 进行整合。
配置类内容具体如下:
从底层往上描述:
如果页面中不使用shiro标签,也可以不写Shiro与Thymeleaf整合配置类。
自定义Realm。创建 安全管理器 DefaultWebSecuityManager。创建 ShiroFilterFactoryBeanShiro与Thymeleaf整合配置类。为了页面支持shiro标签)。
全文最重要最关键的两个点,一个是自定义 Realm 以及shiro 的配置类了。全文核心
1、自定义 Realm 类。
(1)创建 MyRealm 类,继承 AuthorizingRealm 类,必须继承。
(2)实现 doGetAuthorizationInfo、doGetAuthenticationInfo 方法。
要实现登录,doGetAuthenticationInfo 是核心,账号密码在doGetAuthenticationInfo() 方法中做校验。
而授权操作则在 doGetAuthorizationInfo() 方法中操作,比如页面资源限制,用户可以访问哪个资源,执行哪些操作等。都在这个方法中配置。
package com.zkr.mingyu.shiro;import org.apache.shiro.authc.AuthenticationException;import org.apache.shiro.authc.AuthenticationInfo;import org.apache.shiro.authc.AuthenticationToken;import org.apache.shiro.authz.AuthorizationInfo;import org.apache.shiro.realm.AuthorizingRealm;import org.apache.shiro.subject.PrincipalCollection;import java.io.Serializable;public class MyRealm extends AuthorizingRealm implements Serializable {/*** 授权* @param principals* @return*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {System.out.println("执行授权方法: doGetAuthorizationInfo");return null;}/*** 认证* @param token* @return* @throws AuthenticationException*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {System.out.println("执行认证方法: doGetAuthenticationInfo");return null;}}
2、创建shiro配置类。
这个配置类很重要,仔细读一下,一环扣一环。后期密码加密(MD5 + salt + 散列)都在这个类中配置,以及与 Thymeleaf 整合配置等等。
package com.zkr.mingyu.config;import com.zkr.mingyu.shiro.MyRealm;import org.apache.shiro.spring.web.ShiroFilterFactoryBean;import org.apache.shiro.web.mgt.DefaultWebSecurityManager;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;/**标注 @Configuration 注解,* 标注这是一个配置类,* 让项目启动时加载该配置类。*/@Configurationpublic class ShiroConfig {/*** 创建ShiroFilterFactory* 设置权限规则 需要注入securityManage* @param securityManager* @return*/@Beanpublic ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager){ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();shiroFilterFactoryBean.setSecurityManager(securityManager);return shiroFilterFactoryBean;}/*** 创建安全管理器,* 并为 securityManager 注入自定义的 Realm 类* @param realm* @return*/@Beanpublic DefaultWebSecurityManager getSecurityManager(MyRealm realm){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(realm);return securityManager;}/*** 配置自定义 Realm 类* @Bean 将 MyRealm 注入到 Spring 容器当中* @return*/@Beanpublic MyRealm getMyRealm(){return new MyRealm();}}
四、创建页面、Controller、 Service等。
1、在 resource 目录下创建 templeates 文件夹,用来存放 html 页面。并创建index、 login 页面。
index:
login:
2、Controller、 Service、 dao 等
现在的目录结构:
Controller:
package com.zkr.mingyu.controller;import com.zkr.mingyu.entity.User;import com.zkr.mingyu.service.UserService;import org.apache.shiro.SecurityUtils;import org.apache.shiro.authc.IncorrectCredentialsException;import org.apache.shiro.authc.UnknownAccountException;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 javax.annotation.Resource;@Controllerpublic class UserController {@Resourceprivate UserService userService;@RequestMapping("/index")public String login(User user, Model model) {Subject subject = SecurityUtils.getSubject();UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());try {subject.login(token);return "index";} catch (UnknownAccountException e) {model.addAttribute("msg", "用户名错误!");return "login";} catch (IncorrectCredentialsException e) {model.addAttribute("msg", "密码错误!");return "login";}}@RequestMapping("/login")public String index() {return "login";}@RequestMapping("/loginOut")public String loginOut() {Subject subject = SecurityUtils.getSubject();subject.logout();return "login";}@RequestMapping("/update")public String update() {return "/user/update";}}
Service:
package com.zkr.mingyu.service;import com.zkr.mingyu.entity.User;import org.apache.ibatis.annotations.Param;public interface UserService {/*** 根据用户名查找用户* @param userName* @return*/User findByUserName(@Param("username") String userName);}
ServiceImpl:
package com.zkr.mingyu.service;import com.zkr.mingyu.dao.UserMapper;import com.zkr.mingyu.entity.User;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;import javax.annotation.Resource;@Service@Transactional //开启事务public class UserServiceImpl implements UserService {@Resourceprivate UserMapper userMapper;@Overridepublic User findByUserName(String userName) {return userMapper.findByUserName(userName);}}
Dao:
package com.zkr.mingyu.dao;import com.zkr.mingyu.entity.User;import org.apache.ibatis.annotations.Mapper;import org.apache.ibatis.annotations.Param;@Mapperpublic interface UserMapper {/*** 根据用户名查找用户* @param userName* @return*/User findByUserName(@Param("username") String userName);}
entity:
package com.zkr.mingyu.entity;import java.io.Serializable;public class User implements Serializable {/*** id*/private Integer id;/*** 账号*/private String username;/*** 密码*/private String password;/*** 权限*/private String auth;/*** 随机盐*/private String salt;public User() {}public User(Integer id, String username, String password, String auth, String salt) {this.id = id;this.username = username;this.password = password;this.auth = auth;this.salt = salt;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getAuth() {return auth;}public void setAuth(String auth) {this.auth = auth;}public String getSalt() {return salt;}public void setSalt(String salt) {this.salt = salt;}@Overridepublic String toString() {return "User{" +"id=" + id +", username='" + username + '\'' +", password='" + password + '\'' +", auth='" + auth + '\'' +", salt='" + salt + '\'' +'}';}}
Mapper:
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapper PUBLIC "-////DTD Mapper 3.0//EN""/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.zkr.mingyu.dao.UserMapper"><select id="findByUserName" parameterType="String" resultType="user">select id, username, password from user where username = #{username}</select></mapper>
五、修改自定义 MyRealm
自定义 MyRealm 类中 doGetAuthenticationInfo()方法主要配置用户认证。
doGetAuthorizationInfo()方法主要配置用户权限配置,说白了也就是给这个用户赋予什么角色、都有哪些权限。以及对某些资源是否可以访问,如果可以访问,那么该用户可以对这个资源执行哪些操作。操作 == CRUD呗。
package com.zkr.mingyu.shiro;import com.zkr.mingyu.entity.User;import com.zkr.mingyu.service.UserService;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.io.Serializable;public class MyRealm extends AuthorizingRealm implements Serializable {@Autowiredprivate UserService userService;/*** 执行授权逻辑* 权限要和资源对应* 权限声明该用户可以访问系统中哪些资源,对系统中哪些资源进行操作* 不同的用户,拥有不同的权限* @param principals* @return*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {System.out.println("执行授权方法: doGetAuthorizationInfo");SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();User user = (User) principals.getPrimaryPrincipal();User byUserName = userService.findByUserName(user.getUsername());/*** 这个方法中为授权操作* 基本常用方法有:*//*** 控制台打印结果:* getRoles: null* getObjectPermissions: null* getStringPermissions: [user:add]* getClass: class org.apache.shiro.authz.SimpleAuthorizationInfo*///获取用户角色/* System.out.println("getRoles: " + info.getRoles());System.out.println("getObjectPermissions: " + info.getObjectPermissions());//获取用户权限System.out.println("getStringPermissions: " + info.getStringPermissions());System.out.println("getClass: " + info.getClass());*/return null;}/*** 认证* @param token* @return* @throws AuthenticationException*/@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {System.out.println("执行认证方法: doGetAuthenticationInfo");String username = (String) token.getPrincipal();User user = userService.findByUserName(username);if(user == null){return null;}return new SimpleAuthenticationInfo(user,user.getPassword(),this.getName());}}
这是我写的另一个 MyRealm 配置,简单看看了解一下就可。后期还会继续更新,这篇博客开篇也说了,是搭建项目基本框架。后期后在文末添加新的博客链接,最近有时间抓紧谢谢,分享这份知识。
六、配置资源访问权限 ShiroConfig
ShiroFilterFactoryBean 方法主要配置某些资源需要哪些权限才能访问。以及配置默认的登录页面的 URL 地址。访问某些权限不足的资源跳转到哪个页面,都在这里配置。
DefaultWebSecurityManager 是一个安全管理器,后期我们要做密码加密操作(密码 + 盐 + MD5 + 哈希散列)的时候,我们就要在 DefaultWebSecurityManager() 方法中配置自定义的密码 管理器。
package com.zkr.mingyu.config;import com.zkr.mingyu.shiro.MyRealm;import org.apache.shiro.spring.web.ShiroFilterFactoryBean;import org.apache.shiro.web.mgt.DefaultWebSecurityManager;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import java.util.LinkedHashMap;import java.util.Map;/**标注 @Configuration 注解,* 标注这是一个配置类,* 让项目启动时加载该配置类。*/@Configurationpublic class ShiroConfig {/*** 创建ShiroFilterFactory* 设置权限规则 需要注入securityManage* @param securityManager* @return*/@Beanpublic ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager securityManager){ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();shiroFilterFactoryBean.setSecurityManager(securityManager);/*** Shiro内置过滤器,可实现权限相关的拦截器*常用的过滤器:*anon: 无需认证(登录) 可以访问*authc: 必须认证才可以访问*user:如果使用rememberMe的功能可以直接访问*perms: 该资源必须得到资源权限才可以访问*role: 该资源必须得到角色权限才可以访问*/Map<String, String> filterMap = new LinkedHashMap<String,String>();filterMap.put("/login","anon");filterMap.put("/index","anon");filterMap.put("/*","authc");shiroFilterFactoryBean.setLoginUrl("/login");shiroFilterFactoryBean.setUnauthorizedUrl("/noauth");shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);return shiroFilterFactoryBean;}/*** 创建安全管理器,* 并为 securityManager 注入自定义的 Realm 类* @param realm* @return*/@Beanpublic DefaultWebSecurityManager getSecurityManager(MyRealm realm){DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();securityManager.setRealm(realm);return securityManager;}/*** 配置自定义 Realm 类* @Bean 将 MyRealm 注入到 Spring 容器当中* @return*/@Beanpublic MyRealm getMyRealm(){return new MyRealm();}}
代码不多,简单写个 demo 后期一步一步完善,有时间继续更新新的文章。比如密码加密、资源权限细分、会话、缓存等。
后期写好了新的功能,我会放在文章头、文章尾附上链接。看完点个赞,你的赞就是我更新的动力!收藏文章,后期在文章头部有新博客的链接。
附上一句话 (共勉):
滚水看不到倒影,盛怒看不到真相。
如果觉得《Shiro与SpringBoot整合 实现登录拦截 用户认证 用户授权等。实战demo》对你有帮助,请点赞、收藏,并留下你的观点哦!