Shiro框架之新手入门----持续更新中(有案例说明)
如有侵权请联系博主,并进行删除
Shiro简介
hiro是apache的一个开源框架,是一个权限管理的框架,实现 用户认证、用户授权。
spring中有spring security (原名Acegi),是一个权限框架,它和spring依赖过于紧密,没有shiro使用简单。
shiro不依赖于spring,shiro不仅可以实现 web应用的权限管理,还可以实现c/s系统,分布式系统权限管理,shiro属于轻量框架,越来越多企业项目开始使用shiro。
使用shiro实现系统的权限管理,有效提高开发效率,从而降低开发成本。
Shiro运行原理图
模块介绍(图片转载地址:https://img-blog.csdn.net/20161220192235255?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvUVE5OTQ0MDYwMzA=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
Subject:主体,代表了当前操作“用户”,用户不一定指人,也可以是程序,,所有的Subject都绑定到SecurityManager,与Subject的所有交互都会委托给SecurityManager,主体要访问系统,系统需要对主体进行认证、授权。
SecurityManager:安全管理器,可以理解为springmvc里面的DispatcherServlet前端控制器,主体进行认证和授权都是通过securityManager进行。
Realm:域,安全数据源。Shiro从Realm获取数据,相当于SecurityManager需要验证身份,那么他需要从Realm获取相应的用户进行比较确定用户身份是否合法,可以把Realm理解为DataSource
认证过程(转载与:https://blog.csdn.net/mine_song/article/details/61616259?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522160315732219724835862833%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=160315732219724835862833&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_v2~rank_v25-1-61616259.pc_search_result_cache&utm_term=shiro%E6%A1%86%E6%9E%B6%E5%8E%9F%E7%90%86%E8%AE%A4%E8%AF%81%E6%B5%81%E7%A8%8B&spm=1018.2118.3001.4187)
解析
1、通过ini配置文件创建securityManager
2、调用subject.login方法主体提交认证,提交的token
3、securityManager进行认证,securityManager最终由ModularRealmAuthenticator进行认证。
4、ModularRealmAuthenticator调用IniRealm(给realm传入token) 去ini配置文件中查询用户信息
5、IniRealm根据输入的token(UsernamePasswordToken)从 shiro.ini查询用户信息,根据账号查询用户信息(账号和密码)
如果查询到用户信息,就给ModularRealmAuthenticator返回用户信息(账号和密码)
如果查询不到,就给ModularRealmAuthenticator返回null
6、ModularRealmAuthenticator接收IniRealm返回Authentication认证信息
如果返回的认证信息是null,ModularRealmAuthenticator抛出异常(org.apache.shiro.authc.UnknownAccountException)
如果返回的认证信息不是null(说明inirealm找到了用户),对IniRealm返回用户密码 (在ini文件中存在)
和 token中的密码 进行对比,如果不一致抛出异常(org.apache.shiro.authc.IncorrectCredentialsException)
授权流程
解析
1、对subject进行授权,调用方法isPermitted(“permission串”)
2、SecurityManager执行授权,通过ModularRealmAuthorizer执行授权
3、ModularRealmAuthorizer执行realm(自定义的Realm)从数据库查询权限数据
调用realm的授权方法:doGetAuthorizationInfo
4、realm从数据库查询权限数据,返回ModularRealmAuthorizer
5、ModularRealmAuthorizer调用PermissionResolver进行权限串比对
6、如果比对后,isPermitted中"permission串"在realm查询到权限数据中,说明用户访问permission串有权限,否则 没有权限,抛出异常。
接下来为大家进行代码解析
首先按照下图进行项目结构搭建(由于简单就不一步一步教了)
接下来我会我把每个文件中对应的代码写给大家,没有写的就是默认的
1.Shiro.in的配置(有注解很好理解,不懂留言)
[users]
root=123456,admin
test=000000,test
[roles]
admin=*
test=search,add,update
以下是配置详解图
2.ShiroTest类的代码
package com.shiro.test.javase;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
/*
boolean[] isPermitted(String var1):可以查看多个权限,并且可以按照顺序把每一个权限的校验结果封装到布尔类型数组里面
boolean[] isPermittedAll(String var1):判断同时是否具有多个权限
*/
public class ShiroTest {
public static void main(String[] args) {
//指定读取的配置文件
Factory<SecurityManager> factory =
new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
//将SecurityManager绑定到上下文当中,全局设置只需要设置一次
SecurityUtils.setSecurityManager(securityManager);
//subject与当前系统交互的对象
Subject subject = SecurityUtils.getSubject();
//如果把用户改为root,那么所有的权限都可以用,因为把他设置了为了最高权限
UsernamePasswordToken token = new UsernamePasswordToken("test","000000");
//代表用户是否验证过
try {
subject.login(token);
//如果判断成功
if(subject.isAuthenticated()){
System.out.println("登录成功");
//判断角色用hasRole
if(subject.hasRole("admin")){
System.out.println("有admin角色");
}else{
System.out.println("没有admin角色");
}
//isPermitted用来判断用户权限
if(subject.isPermitted("search")){
System.out.println("有search权限");
}else {
System.out.println("没有search权限");
}
if(subject.isPermitted("del")){
System.out.println("有del权限");
}else {
System.out.println("没有del权限");
}
//boolean[] isPermittedAll(String var1):判断同时是否具有多个权限
if(subject.isPermittedAll("add","update")){
System.out.println("有\"add\",\"update\"权限");
}else {
System.out.println("没有\"add\",\"update\"权限");
}
}
} catch (AuthenticationException e) {
e.printStackTrace();
System.out.println("用户名或密码错误,登录失败");
}
}
}
身份验证认证流程(之前的只是一个小入门案列)
流程图
流程解析:
1.首先调用Subject.login(token)进行登录,其会自动委托给SecurityManager,调用之前必须通过SecurityUtils.setSecurityManager();进行设置
2.SecurityManager负责真正的身份逻辑验证,他会委托给Authenticator进行身份验证
3.Authenticator才是真正的身份验证者,ShiroAPI中核心的身份认证入口点,此处可以自定义插入自己的实现
4.Authenticator看你会委托给相应的AuthenticationStrategy进行多Realm身份验证,默认ModulRealmAuthenticator会调用AuthenticationStreategy进行多身份验证
5.Authenticator会把相应的token传入Realm,从Realm获取身份验证信息,如果没有会/抛出异常表示身份验证失败了。此处可以配置多个Realm,将按照相应的顺序及策略进行访问。
接下来给大家截图搞一下小补充
另一种方式实现安全登录注册,只需要
1.首先自己做一个加密算法,代码如下所示
/*
工具类用来加密
*/
public class PasswordUtil {
public static String md5(String source, Object salt) {
//散列次数
int hashIterations = 1024;
SimpleHash simpleHash = new SimpleHash("md5", source,salt,hashIterations);
String md5 = simpleHash.toString();
return md5;
}
2.写一个controller类,进行判断
@Autowired
private Service service;
@ResponseBody
@RequestMapping("/doLogin")
public String diLogin(String username,String password,boolean rememberMe){
User1 all = service.findAll(username);
//if(all==null) throw new UnknownAccountException("zhanghaobucunzai");
if(all==null) {
return "zhanghaobucunzai";
}
String salt = all.getSalt();
String s = PasswordUtil.md5(password, salt);
System.out.println(s);
if(all.getPassword().equals(s)){
System.out.println(all.getPassword());
System.out.println("登录成功");
return "dlcg";
}else
System.out.println("登录失败");
return "dlsb";}
3.接下来还有前端页面
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title>AdminLTE 2 | Log in</title>
<title>Title</title>
<link href="http://apps.bdimg.com/libs/bootstrap/3.3.0/css/bootstrap.min.css" rel="stylesheet">
<!--font-awesome 核心我CSS 文件-->
<link href="//cdn.bootcss.com/font-awesome/4.3.0/css/font-awesome.min.css" rel="stylesheet">
<!-- 在bootstrap.min.js 之前引入 -->
<script src="http://apps.bdimg.com/libs/jquery/2.0.0/jquery.min.js"></script>
<!-- Bootstrap 核心 JavaScript 文件 -->
<script src="http://apps.bdimg.com/libs/bootstrap/3.3.0/js/bootstrap.min.js"></script>
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<![endif]-->
<!-- Google Font -->
</head>
<body class="hold-transition login-page">
<div style="width: 400px ;align-content: center;text-align: center">
<div class="login-box">
<div class="login-logo">
<a href="#"><b>DB-</b>SYS</a>
</div>
<!-- /.login-logo -->
<div class="login-box-body">
<p class="login-box-msg">Sign in to start your session</p>
<div v-show="error.msg" class="alert-danger alert-dismissible">123</div>
<form method="post">
<div class="form-group has-feedback">
<input type="text" id="usernameId" name="username" class="form-control" placeholder="username">
<span class="glyphicon glyphicon-envelope form-control-feedback"></span>
</div>
<div class="form-group has-feedback">
<input type="password" id="passwordId" name="password" class="form-control" placeholder="Password">
<span class="glyphicon glyphicon-lock form-control-feedback"></span>
</div>
<div class="rem-for-agile">
<input type="checkbox" name="rememberMe" class="remember" value="true">rememberMe<br>
<!-- <a href="#">忘记密码</a><br> -->
</div>
<%-- <div class="row">
<div class="col-xs-8">
<div class="checkbox icheck">
<label>
<input type="checkbox" id="rememberId" value="true"> Remember Me
</label>
</div>
</div>--%>
<!-- /.col -->
<div class="col-xs-4">
<button type="button" class="btn btn-primary btn-block btn-flat btn-login" id="login-a">sing in</button>
</div>
<!-- /.col -->
</div>
</form>
</div>
<!-- /.login-box-body -->
</div>
<!-- /.login-box -->
</div>
<script type="text/javascript">
$("#login-a").click(function () {
var val = $("#usernameId").val();
var val1 = $("#passwordId").val();
var val2 = $("#rememberId").val();
$.ajax({
charset: "utf-8",
type: "post",
url: "user/doLogin",
dataType: "text",
data: {
'username': val,
'password': val1,
'rememberId': val2,
},
success: function (data) {
alert(data);
top.location.reload();
}
})
})
</script>
</body>
</html>
4.补充:接口类
package com.launch.service;
import com.launch.entity.User1;
import java.util.List;
public interface Service {
public User1 findAll(String username);
//public void save(User1 user1);
}
封装类
package com.launch.entity;
public class User1 {
private Integer id;
private String username;
private String password;
private String salt;
public String getSalt() {
return salt;
}
public void setSalt(String salt) {
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;
}
@Override
public String toString() {
return "User1{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", salt='" + salt + '\'' +
'}';
}
}
以上就是不用shiro实现登录