什么是Shiro?
shiro是一个开源的安全管理框架,可以完成认证、授权、加密、会话管理、缓存等功能。
Shrio有哪些核心API,各有什么用?
Subject:用户主体,是和应用代码直接进行交互的对象;
SecurityManager:安全管理器,所有与安全相关的操作都会与SecurityManager进行交互,是shiro的核心;
Realm:连接数据库的桥梁
Shiro具有哪些功能?
Authentication:认证登录,验证用户的合法性
Authorization :授权,授予谁具有访问某些资源的权限
Session Management :会话管理,用户登录后的用户信息通过Session Management进行管理,也就是说用户登陆后就是一次会话,在他退出之前,他的所有信息都在会话中
Cryptography:密码模块,提供了一些加密算法
Web Supportweb:支持,可以很容易的集成到web环境中
Caching: 缓存,用户登陆后,其用户信息、所拥有的角色权限不必每次都查,这样 可以提高效率
Concurrency: shiro支持多线程应用的并发验证
Tesing:提供测试支持
Run As:允许一个用户假装另一个用户(如果他们允许的话)的身份进行访问
Remember Me:记住我,一次登陆后,下次就不用在登陆了
上面都是一些理论,接下来才是实际操作
1.创建SpringBoot工程
这里用的sts,Eclipse默认是不集成Spring的,没有的话请自行百度安装Spring插件,该案例用的是SpringBoot2+sts4,整个案例完成后目录如下
2.修改pom.xml文件,添加依赖
org.springframework.boot
spring-boot-starter-web
nekohtml
nekohtml
1.9.6.2
org.apache.shiro
shiro-spring
1.4.0
org.springframework.boot
spring-boot-starter-thymeleaf
org.springframework.boot
spring-boot-devtools
true
org.springframework.boot
spring-boot-starter-tomcat
provided
org.springframework.boot
spring-boot-starter-test
test
com.oracle
ojdbc6
11.1.0.7.0
org.springframework.boot
spring-boot-starter-jdbc
org.mybatis.spring.boot
mybatis-spring-boot-starter
2.0.0
org.apache.commons
commons-dbcp2
org.apache.commons
commons-pool2
org.springframework.boot
spring-boot-starter-parent
2.1.3.RELEASE
pom
com.github.theborakompanioni
thymeleaf-extras-shiro
2.0.0
3.entry(实体类对象)
package com.zfsh.entry;
public class ShiroUser {
private Integer u_id;
private String u_name;
private String u_password;
private String u_dept;
public Integer getU_id() {
return u_id;
}
public void setU_id(Integer u_id) {
this.u_id = u_id;
}
public String getU_name() {
return u_name;
}
public void setU_name(String u_name) {
this.u_name = u_name;
}
public String getU_password() {
return u_password;
}
public void setU_password(String u_password) {
this.u_password = u_password;
}
public String getU_dept() {
return u_dept;
}
public void setU_dept(String u_dept) {
this.u_dept = u_dept;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((u_dept == null) ? 0 : u_dept.hashCode());
result = prime * result + ((u_id == null) ? 0 : u_id.hashCode());
result = prime * result + ((u_name == null) ? 0 : u_name.hashCode());
result = prime * result + ((u_password == null) ? 0 : u_password.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
ShiroUser other = (ShiroUser) obj;
if (u_dept == null) {
if (other.u_dept != null)
return false;
} else if (!u_dept.equals(other.u_dept))
return false;
if (u_id == null) {
if (other.u_id != null)
return false;
} else if (!u_id.equals(other.u_id))
return false;
if (u_name == null) {
if (other.u_name != null)
return false;
} else if (!u_name.equals(other.u_name))
return false;
if (u_password == null) {
if (other.u_password != null)
return false;
} else if (!u_password.equals(other.u_password))
return false;
return true;
}
@Override
public String toString() {
return "ShiroUser [u_id=" + u_id + ", u_name=" + u_name + ", u_password=" + u_password + ", u_dept=" + u_dept
+ "]";
}
public ShiroUser(Integer u_id, String u_name, String u_password, String u_dept) {
super();
this.u_id = u_id;
this.u_name = u_name;
this.u_password = u_password;
this.u_dept = u_dept;
}
public ShiroUser() {
super();
}
}
4.Dao层(查询用户接口)
package com.zfsh.dao;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import com.zfsh.entry.ShiroUser;
@Mapper
public interface ShiroUserDao {
public ShiroUser findByName(@Param(value="u_name")String name);
}
5.添加ShiroUserMapper.xml映射
6.添加Service业务层
package com.zfsh.service;
import org.apache.ibatis.annotations.Param;
import com.zfsh.entry.ShiroUser;
public interface ShiroUserService {
public ShiroUser findByName(@Param(value="u_name")String name);
}
7.添加业务层实现
package com.zfsh.serviceimp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.zfsh.dao.ShiroUserDao;
import com.zfsh.entry.ShiroUser;
import com.zfsh.service.ShiroUserService;
@Service
public class ShiroUserServiceImp implements ShiroUserService{
//引入dao接口方法
@Autowired
private ShiroUserDao shiroUserDao;
@Override
public ShiroUser findByName(String name) {
ShiroUser user = shiroUserDao.findByName(name);
return user;
}
}
8.添加Controller控制器
package com.zfsh.controller;
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;
@Controller
public class ActionController {
@RequestMapping("toLogin")
public String toLogin() {
return "login";
}
@RequestMapping("toAdd")
public String toAdd() {
return "user/adduser";
}
@RequestMapping("toUpdate")
public String toUpdate() {
return "user/updateuser";
}
@RequestMapping("toIndex")
public String toIndex() {
return "index";
}
//未授权页面跳转
@RequestMapping("toUnAuth")
public String unAuth() {
return "unAyth";
}
//登录
@RequestMapping("login")
public String login(String name,String password,Model model) {
//编写Shiro认证操作
//获取Subject
Subject subject = SecurityUtils.getSubject();
//封装用户数据
UsernamePasswordToken token = new UsernamePasswordToken(name,password);
//执行登录方法
try {
subject.login(token);
//没有异常就登录成功
return "redirect:toIndex";
} catch (UnknownAccountException e) {
//登录失败:用户名不存在
model.addAttribute("msg", "用户名不存在");
return "login";
} catch (IncorrectCredentialsException e) {
model.addAttribute("msg", "密码错误");
return "login";
}
}
}
9.编写Shiro类配置
package com.zfsh.shiro;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
/*
* shiro配置类
* */
@Configuration
public class ShiroConfig {
//创建ShiroFilterFactoryBean(用户主体,把操作交给SecurityManager)
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
//添加shiro内置过滤器,可以实现相关权限拦截
/**
* anon:无需认证(登录)就可以访问
* authc:必须认证才可以访问
* user:如果使用rememberMe的功能才可以直接访问
* perms:该资源必须得到资源权限才可以访问
* role:该资源必须得到角色的授权才可以访问
* */
Map filerMap = new LinkedHashMap<>();
// filerMap.put("/toAdd","authc"); //拦截指定页面
// filerMap.put("/toUpdate","authc");
filerMap.put("/toIndex", "anon"); //放行
filerMap.put("/login", "anon");
//授权过滤器
//Tips:当授权拦截后,Shiro会自动跳转到未授权界面
filerMap.put("/toAdd", "perms[user:add]");
filerMap.put("/toUpdate", "perms[user:update]");
//通配符拦截,拦截所有页面
filerMap.put("/*", "authc");
//修改跳转登陆的页面
shiroFilterFactoryBean.setLoginUrl("/toLogin");
//设置未授权页面
shiroFilterFactoryBean.setUnauthorizedUrl("/toUnAuth");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filerMap);
return shiroFilterFactoryBean;
}
//创建DefaultWebSecurityManager(安全管理器,关联Realm)
@Bean(name="securityManager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联Realm
securityManager.setRealm(userRealm);
return securityManager;
}
//创建Realm
@Bean(name="userRealm")
public UserRealm getRealm() {
return new UserRealm();
}
//配置ShiroDialect,用于thymeleaf和shiro标签的配合使用
@Bean
public ShiroDialect getShiroDialect() {
return new ShiroDialect();
}
}
10.自定义Realm
package com.zfsh.shiro;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
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.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import com.zfsh.entry.ShiroUser;
import com.zfsh.service.ShiroUserService;
//自定义Realm
//继承AuthorizingRealm
public class UserRealm extends AuthorizingRealm{
//执行授权逻辑
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("执行授权逻辑");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//添加授权字符串
//该处info.addStringPermission()中字符要与数据库权限字段保持一致
info.addStringPermission("user:add");
info.addStringPermission("user:update");
//获取当前用户的ID
Subject subject = SecurityUtils.getSubject();
ShiroUser user = (ShiroUser) subject.getPrincipal();
ShiroUser dbUser = shiroUserService.findByName(user.getU_name());
System.out.println(dbUser);
info.addStringPermission(dbUser.getU_dept());
return info;
}
@Autowired
private ShiroUserService shiroUserService;
//执行认证逻辑
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行认证逻辑");
//1.判断用户名
UsernamePasswordToken newToken = (UsernamePasswordToken)token;
ShiroUser user = shiroUserService.findByName(newToken.getUsername());
if(user==null) {
//用户名不存在
return null;//Shiro底层会抛出UnkownAccountException
}
//2.判断密码
return new SimpleAuthenticationInfo(user,user.getU_password(),"");
}
}
11.修改application.properties配置文件
#修改默认端口号为8089
server.port=8089
spring.thymeleaf.cache=false
spring.thymeleaf.mode=LEGACYHTML5
#数据源配置
spring.datasource.type=org.apache.commons.dbcp2.BasicDataSource
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.username=ht
spring.datasource.password=hting
spring.datasource.url=jdbc:oracle:thin:@localhost:1521/xe
#起别名
mybatis.type-aliases-package=com.zfsh.entry
#查找自定义目录下的mapper映射文件
mybatis.mapper-locations=classpath:com/zfsh/mapper/*Mapper.xml
12.后台代码以及全部写完,现在编写前台代码
12.1:在src/main/resources/templates下创建user文件目录存储关于用户的html网页
12.2:在templates根目录编写index.html
首页
进入用户添加:用户添加
进入用户更新:用户更新
登录
12.3:在templates根目录编写login.html
登录
登录页面
12.4:在templates根目录下编写unAyth.html未授权显示页面
未授权
对不起,你暂无该权限
12.5:在user目录下编写简单的adduser.html(仅做显示测试用)
添加用户
用户添加
12.6:在user目录下编写简单的updateuser.html(仅做显示测试用)
修改个人信息
用户更新
数据库表的编写:
create table shirouser(
u_id integer, -- 用户ID
u_name varchar2(60), -- 用户姓名
u_password varchar2(100), -- 用户密码
u_dept varchar2(100) -- 用户的权限
);
insert into shirouser(u_id,u_name,u_password,u_dept) values(1,'sansan','aaa','开发部');
commit;
insert into shirouser(u_id,u_name,u_password,u_dept) values(2,'lisi','bbb','人事部');
commit;
全部代码编写完成,现在启动服务器测试
如何启动?
双击ShiroApplication.java这个类,右键Run As-->Spring Boot App即可
打开浏览器在地址栏输入:http://localhost:8089/toIndex,说明未登录拦截成功,进入登录页面。
其他就不测了,代码量有点大!!!