您的位置:首页 > 其它

shiro-权限控制框架

2017-12-20 19:12 369 查看
一. shiro简介

Apache Shiro(发音为“shee-roh”,日语“堡垒(Castle)”的意思)是一个强大易用的Java安全框架,提供了认证、授权、加密和会话管理功能,可为任何应用提供安全保障 - 从命令行应用、移动应用到大型网络及企业应用。

Shiro四要素,提供了保护应用的API:

认证 - 用户身份识别,常被称为用户“登录”;
授权 - 访问控制;
密码加密 - 保护或隐藏数据防止被偷窥;
会话管理 - 每用户相关的时间敏感的状态。

二. shiro特点

易于使用 - 易用性是这个项目的最终目标。应用安全有可能会非常让人糊涂,令人沮丧,并被认为是“必要之恶”【译注:比喻应用安全方面的编程。】。若是能让它简化到新手都能很快上手,那它将不再是一种痛苦了。
广泛性 - 没有其他安全框架可以达到Apache Shiro宣称的广度,它可以为你的安全需求提供“一站式”服务。
灵活性 - Apache Shiro可以工作在任何应用环境中。虽然它工作在Web、EJB和IoC环境中,但它并不依赖这些环境。Shiro既不强加任何规范,也无需过多依赖。
Web能力 - Apache Shiro对Web应用的支持很神奇,允许你基于应用URL和Web协议(如REST)创建灵活的安全策略,同时还提供了一套控制页面输出的JSP标签库。
可插拔 - Shiro干净的API和设计模式使它可以方便地与许多的其他框架和应用进行集成。你将看到Shiro可以与诸如Spring、Grails、Wicket、Tapestry、Mule、Apache Camel、Vaadin这类第三方框架无缝集成。

ini配置权限信息(参考http://shiro.apache.org/configuration.html)

[html] view
plain copy

# =======================

# Shiro INI configuration

# =======================

[main]

[users]

# 设置用户信息

# 语法是 username = password, roleName1, roleName2, …, roleNameN

jiaozi = 123456,role1

[roles]

# 角色信息和角色拥有的权限

#语法是 rolename = permissionDefinition1, permissionDefinition2, …, permissionDefinitionN

#权限的语法 * 表示所有权限 一般语法是 权限类型.权限动作.权限的资源id 比如 user:delete:1 表示拥有删除1号用户的权限 user:delete:*表示删除所有用户权限

admin = *

role1 = user:query:*, user:delete:1

[urls]

# web中的url过滤

maven项目添加支持

[html] view
plain copy

<dependency>

<groupId>org.apache.shiro</groupId>

<artifactId>shiro-core</artifactId>

<version>1.4.0</version>

</dependency>

<!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->

<dependency>

<groupId>commons-logging</groupId>

<artifactId>commons-logging</artifactId>

<version>1.2</version>

</dependency>

测试验证权限过程(参考http://shiro.apache.org/10-minute-tutorial.html)

[html] view
plain copy

package shiro;

import org.apache.shiro.SecurityUtils;

import org.apache.shiro.authc.UsernamePasswordToken;

import org.apache.shiro.config.IniSecurityManagerFactory;

import org.apache.shiro.session.Session;

import org.apache.shiro.subject.Subject;

import org.apache.shiro.util.Factory;

public class TestShiro {

public static void main(String[] args) {

testIni();

}

public static void testIni() {

//从配置文件中读取用户的权限信息

Factory<org.apache.shiro.mgt.SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");

org.apache.shiro.mgt.SecurityManager securityManager = (org.apache.shiro.mgt.SecurityManager)factory.getInstance();

SecurityUtils.setSecurityManager(securityManager);

//获取登录用户信息

Subject currentUser = SecurityUtils.getSubject();

Session session = currentUser.getSession();

session.setAttribute( "保存数据的键", "保存数据的值" );

/**

* 用户包括两部分

// * principals and credentials

* principals(本人)表示用户的标识信息 比如用户名 用户地址等

* credentials(凭证)表示用户用于登录的凭证 比如密码 证书等

*/

//isAuthenticated判断是否登录过

if ( !currentUser.isAuthenticated() ) {

//令牌 用户名和密码 其实就是credentials

UsernamePasswordToken token = new UsernamePasswordToken("jiaozi", "123456");

token.setRememberMe(true);

//开始登录操作 操作后 isAuthenticated就是true

currentUser.login(token);

System.out.println("登录成功");

//判断是否有某个角色

if(currentUser.hasRole("role1")){

System.out.println("拥有角色role1");

}

if(currentUser.isPermitted("user:query:1")){

System.out.println("拥有查询1号用户权限");

}

}

currentUser.logout();

}

}

一般权限信息存在于是数据库
我这里模拟放在内存变量map中

添加realm的实现类

[html] view
plain copy

package shiro;

import java.util.HashMap;

import java.util.HashSet;

import java.util.Map;

import java.util.Set;

import org.apache.shiro.authc.AuthenticationException;

import org.apache.shiro.authc.AuthenticationInfo;

import org.apache.shiro.authc.AuthenticationToken;

import org.apache.shiro.authc.SimpleAccount;

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;

/**

* 自定义realm的实现

* @author jiaozi

*

*/

public class MyRealm extends AuthorizingRealm {

//用于存放用户信息

static Map<String,String> userList=null;

//用于存放橘色信息

static Map<String,String> roleList=null;

//每个realm都有一个名字

static String REALM_NAME="myrealm";

static{

//这里也可以从数据库读取

//模拟用户

userList=new HashMap();

userList.put("zs", "123456,role2,role3");

//模拟权限

roleList=new HashMap();

roleList.put("role2","user:query:*");

roleList.put("role3", "user:*");

}

/**

* 支持哪种令牌

*/

@Override

public boolean supports(AuthenticationToken token) {

// TODO Auto-generated method stub

return token instanceof UsernamePasswordToken;

}

/**

* 获取权限过程

*/

@Override

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

//获取用户名

String userName=principals.getPrimaryPrincipal().toString();

//构建权限的类

SimpleAuthorizationInfo sai=new SimpleAuthorizationInfo();

Set<String> proleList=new HashSet<String>();

Set<String> stringPermissions=new HashSet<String>();

if(userList.containsKey(userName)){

String[] roles=userList.get(userName).toString().split(",");

for(int i=1;i<roles.length;i++){

proleList.add(roles[i]);

String pp=roleList.get(roles[i]);

String[] ppArry=pp.split(",");

for(int j=0;j<ppArry.length;j++){

stringPermissions.add(ppArry[j]);

}

}

}

sai.setRoles(proleList);

sai.setStringPermissions(stringPermissions);

return sai;

}

/**

* 认证过程

*/

@Override

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

UsernamePasswordToken upt=(UsernamePasswordToken)token;

String userName=token.getPrincipal().toString();

String password=String.valueOf(upt.getPassword());

if(userList.containsKey(userName)){

String realPwd=userList.get(userName).toString().split(",")[0];

if(realPwd.equals(password)){

SimpleAccount sa=new SimpleAccount(userName,password,"REALM_NAME");

return sa;

}

}

return null;

}

}

ini配置文件中配置该realm main的部分添加

[html] view
plain copy

[main]

# 定义securityManager, Realms and 其他

myRealm= shiro.MyRealm

securityManager.realms=$iniRealm , $myRealm

#iniRealm是内置的默认realm读取ini文件的传入 如果自定义了realm该realm被替代 如果还想使用可以直接引用

#如果有多个realm 策略是所有的realm都要验证通过 还是只需要一个验证通过 我这里只需要一个验证通过 默认是所有

#参考http://shiro.apache.org/authentication.html

authcStrategy = org.apache.shiro.authc.pam.FirstSuccessfulStrategy

securityManager.authenticator.authenticationStrategy = $authcStrategy

在之前例子基础上进行拓展 添加war项目 添加shiro-web依赖

[html] view
plain copy

<dependency>

<groupId>org.apache.shiro</groupId>

<artifactId>shiro-web</artifactId>

<version>1.4.0</version>

</dependency>

web.xml添加shiro支持的过滤器和ini文件路径配置参数

[html] view
plain copy

<context-param>

<param-name>shiroConfigLocations</param-name>

<param-value>/WEB-INF/shiro.ini</param-value>

</context-param>

<listener>

<listener-class>org.apache.shiro.web.env.EnvironmentLoaderListener</listener-class>

</listener>

<filter>

<filter-name>ShiroFilter</filter-name>

<filter-class>org.apache.shiro.web.servlet.ShiroFilter</filter-class>

</filter>

<filter-mapping>

<filter-name>ShiroFilter</filter-name>

<url-pattern>/*</url-pattern>

<dispatcher>REQUEST</dispatcher>

<dispatcher>FORWARD</dispatcher>

<dispatcher>INCLUDE</dispatcher>

<dispatcher>ERROR</dispatcher>

</filter-mapping>

添加几个html和jsp用于测试

login.html添加用于登录的表单

[html] view
plain copy

<form action="loginServlet" method="post">

用戶名 :<input type="text" name="userName"/>

密碼:<input type="text" name="password"/>

<input type="submit">

</form>

添加query.jsp用于当登录成功后跳转的页面

[html] view
plain copy

<body>

這是測試查詢頁面

</body>

添加add.jsp用于测试拥有某个角色才能进入的页面

[html] view
plain copy

<body>

這是測試新增頁面

</body>

添加一个检测没有角色失败页面 un.jsp

[html] view
plain copy

<body>

没有权限

</body

t添加一个登录的servlet

web.xml

[html] view
plain copy

<servlet>

<description></description>

<display-name>LoginServlet</display-name>

<servlet-name>LoginServlet</servlet-name>

<servlet-class>cn.et.web.LoginServlet</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>LoginServlet</servlet-name>

<url-pattern>/loginServlet</url-pattern>

</servlet-mapping>

servlet类

[html] view
plain copy

package cn.et.web;

import java.io.IOException;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import org.apache.shiro.SecurityUtils;

import org.apache.shiro.authc.AuthenticationException;

import org.apache.shiro.authc.UsernamePasswordToken;

import org.apache.shiro.subject.Subject;

public class LoginServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

/**

* 登录进入方法

*/

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

Subject subject = SecurityUtils.getSubject();

UsernamePasswordToken upt=new UsernamePasswordToken(request.getParameter("userName"), request.getParameter("password"));

try {

subject.login(upt);

request.getRequestDispatcher("/query.jsp").forward(request, response);

} catch (AuthenticationException e) {

request.getRequestDispatcher("/login.html").forward(request, response);

}

}

/**

* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)

*/

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

// TODO Auto-generated method stub

doGet(request, response);

}

}

修改shiro.ini添加路径过滤

[html] view
plain copy

# =======================

# Shiro INI configuration

# =======================

[main]

# 定义securityManager, Realms and 其他

myRealm= shiro.MyRealm

securityManager.realms=$iniRealm , $myRealm

#如果有多个realm 策略是所有的realm都要验证通过 还是只需要一个验证通过 我这里只需要一个验证通过 默认是所有

authcStrategy = org.apache.shiro.authc.pam.FirstSuccessfulStrategy

securityManager.authenticator.authenticationStrategy = $authcStrategy

authc.loginUrl = /login.html

#如果沒有登錄 會先跳轉到loginUrl 如果登錄了沒權限 跳轉到unauthorizedUrl指定頁面

roles.loginUrl= /login.html

roles.unauthorizedUrl= /un.html

[users]

# 设置用户信息

# 语法是 username = password, roleName1, roleName2, …, roleNameN

jiaozi = 123456,role1

[roles]

# 角色信息和角色拥有的权限

#语法是 rolename = permissionDefinition1, permissionDefinition2, …, permissionDefinitionN

#权限的语法 * 表示所有权限 一般语法是 权限类型.权限动作.权限的资源id 比如 user:delete:1 表示拥有删除1号用户的权限 user:delete:*表示删除所有用户权限

admin = *

role1 = user:query:*, user:delete:1

[urls]

# web中的url过滤

#語法是 某個路徑 = 怎麼樣過濾1,過濾2 常用的過濾有

# anon 匿名用戶

# authc 表示用戶和密碼驗證過濾 類 org.apache.shiro.web.filter.authc.FormAuthenticationFilter 沒有登錄自動跳轉到登錄頁

# perms 是否擁有某些權限過濾 類 org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter 用法 perms["remote:invoke"]

# roles是否擁有某個角色 org.apache.shiro.web.filter.authz.RolesAuthorizationFilter 用法roles[administrator]

# user 是否是某個用戶 org.apache.shiro.web.filter.authc.UserFilter

# 也可以在main中自定義filter url就可以應用 參考http://shiro.apache.org/web.html#programmatic-support

/login.html = anon

/loginServlet = anon

/query.jsp = authc

/add.jsp = roles[role2]

添加maven的jsp支持和shiro-spring和springboot

[html] view
plain copy

<parent>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-parent</artifactId>

<version>1.5.9.RELEASE</version>

</parent>

<dependencies>

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-web</artifactId>

</dependency>

<dependency>

<groupId>org.apache.tomcat.embed</groupId>

<artifactId>tomcat-embed-jasper</artifactId>

</dependency>

<dependency>

<groupId>org.apache.shiro</groupId>

<artifactId>shiro-core</artifactId>

<version>1.4.0</version>

</dependency>

<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-web -->

<dependency>

<groupId>org.apache.shiro</groupId>

<artifactId>shiro-web</artifactId>

<version>1.4.0</version>

</dependency>

<!-- https://mvnrepository.com/artifact/org.apache.shiro/shiro-spring -->

<dependency>

<groupId>org.apache.shiro</groupId>

<artifactId>shiro-spring</artifactId>

<version>1.4.0</version>

</dependency>

<!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->

<dependency>

<groupId>commons-logging</groupId>

<artifactId>commons-logging</artifactId>

&n
20000
bsp; <version>1.2</version>

</dependency>

</dependencies>

html和realm还是之前的 ini文件被替换成在spring中配置 添加配置的bean

[html] view
plain copy

package cn.et.boot;

import java.util.HashMap;

import java.util.LinkedHashSet;

import java.util.Map;

import org.apache.shiro.realm.Realm;

import org.apache.shiro.spring.LifecycleBeanPostProcessor;

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;

import org.apache.shiro.web.mgt.DefaultWebSecurityManager;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.boot.web.servlet.FilterRegistrationBean;

import org.springframework.context.annotation.Bean;

import org.springframework.context.annotation.Configuration;

import org.springframework.web.filter.DelegatingFilterProxy;

import shiro.MyRealm;

@Configuration

public class Config {

/**

* 等價于 web.xml配置

* 自動 將 /*的請求 委託給spring容器中 bean名字和filter-name一致的bean處理

*

<filter>

<filter-name>shiroFilter</filter-name>

<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>

<init-param>

<param-name>targetFilterLifecycle</param-name>

<param-value>true</param-value>

</init-param>

</filter>

<filter-mapping>

<filter-name>shiroFilter</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

* @return

*/

@Bean

public FilterRegistrationBean webShiroFilter(){

FilterRegistrationBean frb=new FilterRegistrationBean();

DelegatingFilterProxy dfp=new DelegatingFilterProxy();

frb.setFilter(dfp);

frb.setName("shiroFilter");

LinkedHashSet<String> linkedHashSet = new LinkedHashSet<String>();

linkedHashSet.add("/*");

frb.setUrlPatterns(linkedHashSet);

Map<String, String> initParameters=new HashMap<String, String>();

initParameters.put("targetFilterLifecycle", "true");

frb.setInitParameters(initParameters);

return frb;

}

/**

* 配置我的realm

* @return

*/

@Bean

public Realm myRealm(){

return new MyRealm();

}

/**

* 定義默認的securityManager

* @return

*/

@Bean

public DefaultWebSecurityManager securityManager(@Autowired Realm myRealm){

DefaultWebSecurityManager dwm=new DefaultWebSecurityManager();

dwm.setRealm(myRealm);

return dwm;

}

/**

* 定義和過濾器一致名字的ShiroFilterFactoryBean

*/

@Bean

public ShiroFilterFactoryBean shiroFilter(@Autowired org.apache.shiro.mgt.SecurityManager securityManager){

ShiroFilterFactoryBean sffb=new ShiroFilterFactoryBean();

sffb.setSecurityManager(securityManager);

sffb.setLoginUrl("/login.html");

sffb.setUnauthorizedUrl("/un.jsp");

Map<String, String> urls=new HashMap<String, String>();

/*

* 定义url

/login.html = anon

/loginServlet = anon

/query.jsp = authc

/add.jsp = roles[role2]

* */

urls.put("/login.html", "anon");

urls.put("/loginServlet", "anon");

urls.put("/query.jsp", "authc");

urls.put("/add.jsp", "roles[role1]");

sffb.setFilterChainDefinitionMap(urls);

return sffb;

}

/**

* 定義後置處理器

* @return

*/

@Bean

public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){

return new LifecycleBeanPostProcessor();

}

}

将之前servlet替换成springmvc的controller

[html] view
plain copy

package cn.et.boot;

import org.apache.shiro.SecurityUtils;

import org.apache.shiro.authc.AuthenticationException;

import org.apache.shiro.authc.UsernamePasswordToken;

import org.apache.shiro.subject.Subject;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

@Controller

public class LoginController {

@RequestMapping("loginServlet")

public String login(String userName,String password){

Subject subject = SecurityUtils.getSubject();

UsernamePasswordToken upt=new UsernamePasswordToken(userName,password);

try {

subject.login(upt);

return "/query.jsp";

} catch (AuthenticationException e) {

return "redirect:/login.html";

}

}

}

添加一个springboot启动类 just run

[html] view
plain copy

package cn.et.boot;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication

public class Main {

public static void main(String[] args) {

SpringApplication.run(Main.class, args);

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息