SSM整合Shiro实现认证、加密和授权
学习目标
1、shiro介绍2、SSM整合Shiro
学习内容
1.shiro介绍
1.1介绍shiro是一个java的 安全框架,apache的一个开源框架。简单易用。实现的功能:认证、授权、会话管理和密码加密等功能。可以用在java SE环境中,java EE环境中。1.2shrio功能特点:

Authentication:认证
Authorization:授权
Session Management:session会话管理
Cryptography:密码加密
Caching:快取
web Support:web支援
Concurrency:多执行绪、并发
Testing:测试
Run as: 以 某种 身份 执行
Remember Me:记住我
1.3shiro执行原理

可以看到:应用程序码直接互动的物件是Subject,也就是说Shiro的对外API核心就是Subject;其每个API的含义:
Subject:主体,抽象的身份:当前的使用者,网络爬虫、第三方的程式、程序
SecurityManager:安全管理器,管理所有的subjects;委托给SecurityManager进行安全管理。门面模式:Facade;需求----》委托人————分发下去;
Realm:域,资料域。获取数据库中使用者资讯、许可权资讯。
接下来我们来从Shiro内部来看下Shiro的架构,如下图所示:

1.4过滤器
Shiro内建了很多预设的拦截器,比如身份验证、授权等相关的。预设拦截器可以参考org.apache.shiro.web.filter.mgt.DefaultFilter中的列举拦截器:
解释:
/admins/**=anon # 表示该 uri 可以匿名访问/admins/**=auth # 表示该 uri 需要认证才能访问/admins/**=authcBasic # 表示该 uri 需要 httpBasic 认证/admins/*=perms[user:add:] # 表示该 uri 需要认证使用者拥有 user:add:* 许可权才能访问/admins/**=port[8081] # 表示该 uri 需要使用 8081 埠/admins/=rest[user] # 相当于 /admins/=perms[user:method],其中,method 表示 get、post、delete 等/admins/**=roles[admin] # 表示该 uri 需要认证使用者拥有 admin 角色才能访问/admins/**=ssl # 表示该 uri 需要使用 https 协议/admins/**=user # 表示该 uri 需要认证或通过记住我认证才能访问/logout=logout # 表示登出,可以当作固定配置注意:
anon,authcBasic,auchc,user 是认证过滤器。perms,roles,ssl,rest,port 是授权过滤器。
2、shiro的hello world
参考官网的示例public static void main(String[] args) {
//初始化工厂例项
Factory factory = new IniSecurityManagerFactory("classpath:shiro.ini");
//例项化一个安全管理器
SecurityManager securityManager = factory.getInstance();
//设定安全管理器
SecurityUtils.setSecurityManager(securityManager);
//获取Subject:当前使用者
Subject currentUser = SecurityUtils.getSubject();
//从session中查询中当前登入的使用者资讯 ,类似于登入时从session中获取登入资讯
Session session = currentUser.getSession();
session.setAttribute("someKey", "aValue");
String value = (String) session.getAttribute("someKey");
if (value.equals("aValue")) {
log.info("Retrieved the correct value! [" + value + "]");
}
// let's login the current user so we can check against roles and permissions:
//如果当前使用者还没有认证
if (!currentUser.isAuthenticated()) {
//身份令牌:使用者名称和密码 ;实际从前台登入页面获取
UsernamePasswordToken token = new UsernamePasswordToken("root", "secret");
//记住我
token.setRememberMe(true);
try {
//模拟登入 :会将身份令牌资讯传给安全管理器,委托给安全管理器进行认证
currentUser.login(token);
} catch (UnknownAccountException uae) { //账号不存在
log.info("没有该账号 " + token.getPrincipal());
} catch (IncorrectCredentialsException ice) { //密码不匹配
log.info("Password for account " + token.getPrincipal() + " was incorrect!");
} catch (LockedAccountException lae) {//账号锁定
log.info("The account for username " + token.getPrincipal() + " is locked. " +
"Please contact your administrator to unlock it.");
}
// ... catch more exceptions here (maybe custom ones specific to your application?
catch (AuthenticationException ae) { //认证异常
//unexpected condition? error?
}
}
//say who they are:
//print their identifying principal (in this case, a username):
log.info("使用者 [" + currentUser.getPrincipal() + "] 登入成功.");
//角色判断
//test a role:
if (currentUser.hasRole("schwartz")) {
log.info("May the Schwartz be with you!");
} else {
log.info("Hello, mere mortal.");
}
//许可权判断
//test a typed permission (not instance-level)
if (currentUser.isPermitted("lightsaber:weild")) {
log.info("You may use a lightsaber ring. Use it wisely.");
} else {
log.info("Sorry, lightsaber rings are for schwartz masters only.");
}
//a (very powerful) Instance Level permission:
if (currentUser.isPermitted("winnebago:drive:eagle5")) {
log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " +
"Here are the keys - have fun!");
} else {
log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
}
//all done - log out!
//登出
currentUser.logout();
System.exit(0);
}
3、SSM整合+shiro认证
步骤:1、新增依赖
org.apache.shiro
shiro-core
${shiro.version}
org.apache.shiro
shiro-ehcache
${shiro.version}
org.apache.shiro
shiro-spring
${shiro.version}
org.apache.shiro
shiro-web
${shiro.version}
2、配置web.xml中的shiro的过滤器
shiroFilter
org.springframework.web.filter.DelegatingFilterProxy
targetFilterLifecycle
true
shiroFilter
/*
3、配置shiro.xml档案
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
/index.jsp=anon
/user/login=anon
/user/logout=logout
/static/**=anon
/admin/**= authc
登入认证:
4、设定控制器中的登入方法:
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/login")
public String login(User user){
System.out.println("登入:"+user);
//身份令牌
UsernamePasswordToken token=new UsernamePasswordToken(user.getUsername(), user.getPassword());
Subject currentUser=SecurityUtils.getSubject();
token.setRememberMe(true);
try {
currentUser.login(token);
} catch (UnknownAccountException e){
throw new UnknownAccountException(e.getMessage());
} catch (IncorrectCredentialsException e){
throw new IncorrectCredentialsException(e.getMessage());
}catch (AuthenticationException e) {
throw new AuthenticationException(e.getMessage());
}
return "admin/index";
}
}
5、建立Realm类
public class MyRealm extends AuthenticatingRealm {
// 实现使用者的身份认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("开始身份认证:");
//获取使用者名称 :从登入页面传递过来的使用者名称
// authenticationToken.getPrincipal();
UsernamePasswordToken token= (UsernamePasswordToken) authenticationToken;
String username=token.getUsername();//获取使用者名称
//查询资料是否存在使用者名称
System.out.println("使用者名称:"+username);
// 根据使用者名称:账号;从资料中查询该使用者名称对应的密码
String password="abcdef";
Object principal=username;
Object credentials=password;
String realmName=getName();
//存的从数据库取出来的使用者名称字 和密码
AuthenticationInfo info=new SimpleAuthenticationInfo(principal,credentials,realmName);
return info;
}
}
密码加密
常见的 对称加密 算法主要有 DES、3DES、AES 等,常见的 非对称算法 主要有 RSA、DSA 等,杂凑算法 主要有 SHA-1、MD5 等。
使用杂凑算法:
hashAlgorithmName:加密的算法名称 MD5(不可逆的)
credentials:密码
salt:盐(使用者名称+随机数字)
hashIterations:迭代次数
new SimpleHash(hashAlgorithmName, credentials, salt, hashIterations)
realm中的认证:
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("开始身份认证:");
//获取使用者名称 :从登入页面传递过来的使用者名称
// authenticationToken.getPrincipal();
UsernamePasswordToken token= (UsernamePasswordToken) authenticationToken;
String username=token.getUsername();//获取使用者名称
//查询资料是否存在使用者名称
System.out.println("使用者名称:"+username);
// 根据使用者名称:账号;从资料中查询该使用者名称对应的密码
//模拟从数据库中取出加密后的密码
Object credentials="f7b5cd56c1e2d49e5b69c9dde7679ca4";
if("test".equals(username)){
//123456
credentials="4292bb58be34c59d28a0dcbd11932d49";
} else if("admin".equals(username)){
//abcdef
credentials="f7b5cd56c1e2d49e5b69c9dde7679ca4";
}
Object principal=username;
// Object credentials=password;
String realmName=getName();
//盐 :以使用者名称充当盐值
ByteSource credentialsSalt= ByteSource.Util.bytes(username);
//从数据库取出来的 使用者名称字 和 加密后的密码
AuthenticationInfo info=new SimpleAuthenticationInfo(principal,credentials,credentialsSalt,realmName);
return info;
}
授权
不同的使用者---》角色----》许可权
资料表:使用者表、角色表、选单表,使用者角色关联表,角色选单关联表;
A角色 A资源(表、实体)) create
update
delete
query
资源:动作
shiro支援3种方式的授权验证:
1、编码
验证角色
//get the current Subject
Subject currentUser = SecurityUtils.getSubject();
if (currentUser.hasRole("administrator")) {
//show a special button•
} else {
//don’t show the button?)•
}
验证许可权
Subject currentUser = SecurityUtils.getSubject();
Permission printPermission = new PrinterPermission("laserjet3000n","print");
If (currentUser.isPermitted(printPermission)) {
//do one thing (show the print button?)•
} else {
//don’t show the button?
}
String perm = "printer:print:laserjet4400n";
if(currentUser.isPermitted(perm)){
//show the print button?
} else {
//don’t show the button?
}
2、注解
//Will throw an AuthorizationException if none
//of the caller’s roles imply the Account
//'create' permission
@RequiresPermissions("account:create")•
public void openAccount( Account acct ) {
//create the account
}
//Throws an AuthorizationException if the caller
//doesn’t have the ‘teller’ role:
@RequiresRoles( "teller" )
public void openAccount( Account acct ) {
//do something in here that only a teller
//should do
}
需要开启注解配置:
3、标签
Click here to manage users
No user management for you!
修改MyRealm类:
//实现授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
Object username= principals.getPrimaryPrincipal();
//根据使用者名称获取对应的角色,模拟从数据库中获取角色列表
Set roles=new HashSet();
roles.add("admin");
roles.add("guest");
roles.add("test");
// 返回的对应的 AuthorizationInfo的实现类的物件
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
//系结角色列表
info.setRoles(roles);
// 系结许可权列表
// info.setStringPermissions();
return info;
}
在相应的控制器中增加注解方式的角色或者许可权判断,根据返回的异常进行处理。