shiro详解
shiro:Apache Shiro 是Java 的一个安全框架。Shiro 可以非常容易的开发出足够好的应用,其不仅可以用在JavaSE 环境,也可以用在JavaEE 环境。Shiro 可以帮助我们完成:认证【登陆】、授权【权限】、加密【密码】、会话管理、与Web 集成、缓存等。
shiro执行流程:
subject: 理解为用户;
securityManager: 安全管理 它是核心组件。
Authenticator: 认证器
Authorizer: 授权器。
Realm: 理解为和数据库交互的一个组件。
快速入门
1,引入依赖:shiro-core
2. 创建一个ini文件用来表示数据库
[users]===表示user表
zs=123456
ls=123456
3. 测试
package com.ykq.ini;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.text.IniRealm;
import org.apache.shiro.subject.Subject;
/**
* @Author 呆鸡
* @Date 2021/4/1 14:42
* @Version 1.0
*/
public class Test {
public static void main(String[] args) {
//得到SecurityManager对象
DefaultSecurityManager securityManager=new DefaultSecurityManager();
//设置securityManager管理的realm对象
securityManager.setRealm(new IniRealm("classpath:shiro.ini"));
//3.把securityManager绑定到SecurityUtils
SecurityUtils.setSecurityManager(securityManager);
//4. 获取Subject对象
Subject subject = SecurityUtils.getSubject();
//封装账户和密码
UsernamePasswordToken token=new UsernamePasswordToken("zs","123456");
try {
//5.执行认证功能
subject.login(token);//token:
System.out.println("账户和密码正确");
}catch (Exception e){
e.printStackTrace();
System.out.println("账户或密码错误");
}
//isAuthenticated()判断当前的用户是否被认证
System.out.println("是否认证成功:"+subject.isAuthenticated());
//退出登陆
subject.logout();
//isAuthenticated()判断当前的用户是否被认证
System.out.println("是否认证成功:"+subject.isAuthenticated());
}
}
认证流程
- 首先调用 Subject.login(token) 进行登录,其会自动委托给 Security Manager,调用之前必须通过
- SecurityUtils.setSecurityManager() 设置;
- SecurityManager 负责真正的身份验证逻辑;它会委托给 Authenticator 进行身份验证;
- Authenticator 才是真正的身份验证者,Shiro API 中核心的身份认证入口点,此处可以自定义插入自己的实现;
- Authenticator 可能会委托给相应的 AuthenticationStrategy 进行多 Realm 身份验证,默认 ModularRealmAuthenticator 会调用 AuthenticationStrategy 进行多 Realm 身份验证;
- Authenticator 会把相应的 token 传入 Realm,从 Realm 获取身份验证信息,如果没有返回 / 抛出异常表示身份验证失败了。
- 此处可以配置多个 Realm,将按照相应的顺序及策略进行访问。
正常开发中连接的是数据库,我们需要定义自己的realm类
- 创建自己的realm类
- 继承AuthorizingRealm
package com.ykq.myrealm;
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.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import java.util.List;
/**
* @Author 闫克起
* @Date 2021/4/1 16:04
* @Version 1.0
*/
public class MyRealm extends AuthorizingRealm {
private UserService userService=new UserService();
//当执行授权时调用该方法。
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
User user = (User) principals.getPrimaryPrincipal();
System.out.println("授权方法的执行~~~~~~~~~~~~~~~~~~~~~~~"+user);
//根据用户id查询该用户具有的权限码
List<String> permissions=userService.findPermissionByUserId(user.getId());
//绑定权限
SimpleAuthorizationInfo info=new SimpleAuthorizationInfo();
info.addStringPermissions(permissions);
return info;
}
//当执行认证功能时调用该方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("认证方法的执行~~~~~~~~~~~~~~~~~~~~~~~");
String loginname=token.getPrincipal().toString();//得到账户 唯一的标识。
//调用service中方法 根据用户名查询用户信息。
User user=userService.findByLoginname(loginname);
if(user!=null){
//Object principal, 账号 可以被登陆成功后获取 还可以传给授权的方法
// Object credentials, 从数据库中查询的密码
// String realmName:当前realm的名称
SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(user,user.getPassword(),this.getName());
return info;
}
return null;
}
}
shiro的密码加密
MD5加密:非对称加密
public static void main(String[] args) {
//Md5Hash md5Hash=new Md5Hash("123456");//把明文123456 转化为密文
//System.out.println(md5Hash.toString());//e10adc3949ba59abbe56e057f20f883e 网上的把密文和明文记录起来了。
//把明文+盐 再进行加密---密文
// Md5Hash md5Hash=new Md5Hash("123456","xjp?");//123456ykq?
// System.out.println(md5Hash.toString());
//把明文+盐 再进行加密---密文---->再经过1024次散列【1024加密】
Md5Hash md5Hash=new Md5Hash("123456","xjp?",1024);
System.out.println(md5Hash.toString());
}
改进自己的realm类
//当执行认证功能时调用该方法
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("认证方法的执行~~~~~~~~~~~~~~~~~~~~~~~");
String loginname=token.getPrincipal().toString();//得到账户 唯一的标识。
//调用service中方法 根据用户名查询用户信息。
User user=userService.findByLoginname(loginname);
if(user!=null){
//Object principal, 账号 可以被登陆成功后获取 还可以传给授权的方法
// Object credentials, 从数据库中查询的密码
// String realmName:当前realm的名称
ByteSource salt = ByteSource.Util.bytes("xjp?");//得到盐
SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(user,user.getPassword(),salt,this.getName());
return info;
}
return null;
}