Shiro简介及与spring集成

来源:互联网 发布:java drawimage 编辑:程序博客网 时间:2024/05/16 11:43

【IT168 技术】Apache Shiro是一个强大的,易用的Java安全框架。它被用作于认证,授权,加密,session管理。依赖于Shiro简单易懂的API,就可以快速的构建包括手机,大型web和商业应用。

1、权限基础

两个基本的概念

——安全实体:就是被权限系统保护的对象,比如工资数据。

——权限:就是需要被校验的行为,比如查看、修改等。

分配权限:

——把对某些安全实体的某些权限分配给某些人员。

——是向数据库里面添加数据、或是维护数据的过程

权限验证(权限匹配):

——判断某个人员或程序对某个安全实体是否拥有某个或某些权限。

——从数据库中获取相应数据进行匹配的过程。

权限的继承性:

如果多个安全实体存在包含关系,而某个安全实体没有权限限制,则它会继承包含它的安全实体的相应权限。

权限的最近匹配原则:

如果多个安全实体存在包含关系,而某个安全实体没有权限限制,那么它会向上寻找并匹配相应权限限制,直到找到一个离这个安全实体最近的拥有相应权限限制的安全实体为止。如果把整个层次结构都寻找完了都没有匹配到相应权限限制的话,那就说明所有人对这个安全实体都拥有这个相应的权限限制。

2、Shiro架构与功能介绍

Shiro简介及与spring集成

Authentication: 认证。即验证是哪个用户登录。

Authorization:也被称为访问控制,即决定当前登录用户是否有权限去访问受保护的资源。

Cryptography:通过加密算法保护数据安全。

Session Management: 当用户使用你的应用是自身携带的数据。

Web Integration: 虽然Shiro是设计用来确保任何基于JVM的应用,但是很多时候是用来保护Web应用。

Integrations:可以很好的集成其他的技术和框架。

下图展示了Shiro的的四大核心功能和所支持的其它特性。

Shiro简介及与spring集成

三大核心组件

Shiro简介及与spring集成

Subject:正与系统进行交互的人,或某一个第三方服务。所有Subject实例都被绑定到(且这是必须的)一个SecurityManager上。

SecurityManager:Shiro架构的心脏,用来协调内部各安全组件,管理内部组件实例,并通过它来提供安全管理的各种服务。当Shiro与一个Subject进行交互时,实质上是幕后的SecurityManager处理所有繁重的Subject安全操作。

Realms:本质上是一个特定安全的DAO。当配置Shiro时,必须指定至少一个Realm用来进行身份验证和/或授权。Shiro提供了多种可用的Realms来获取安全相关的数据。如关系数据库(JDBC),INI及属性文件等。可以定义自己Realm实现来代表自定义的数据源。

Shiro架构图

Shiro简介及与spring集成

Authenticator:执行对用户的身份验证(登录)的组件。Authenticator从一个或多个Realm中获得数据以验证用户的身份。

若存在多个realm,则接口AuthenticationStrategy会确定什么样算是验证成功(例如,如果一个Realm成功,而其他的均失败,是否登录成功)。

Authorizer:验证用户能否访问应用中的受保护的资源

SessionManager:可在任何应用或架构层一致地使用 Session API

SessionDAO:SessionManager执行Session持久化(CRUD)操作。

CacheManager :对Shiro组件提供缓存支持。

Cryptography:Shiro的API大幅度简化Java API中繁琐的密码加密

Realms:Shiro通过Realms来获取相应的安全数据

Realm 实现关系:

Shiro简介及与spring集成

认证

Shiro简介及与spring集成

Shiro简介及与spring集成

Step 1:应用程序代码调用Subject.login方法,传递创建好的包含终端用户的Principals(身份)和Credentials(凭证)的AuthenticationToken实例

Step 2:Subject实例,通常为DelegatingSubject(或子类)委托应用程序的SecurityManager通过调用securityManager.login(token)开始真正的验证。

Step3:SubjectManager接收token,调用内部的Authenticator实例调用 authenticator.authenticate(token)。 Authenticator通常是一个ModularRealmAuthenticator实例,支持在身份验证中协调一个或多个Realm实例。

Step 4:如果应用程序中配置了一个以上的Realm,ModularRealmAuthenticator实例将利用配置好的AuthenticationStrategy来启动Multi-Realm认证尝试。在Realms被身份验证调用之前,期间和以后,AuthenticationStrategy被调用使其能够对每个Realm的结果作出反应。

Step 5:每个配置的 Realm 用来帮助看它是否支持提交的AuthenticationToken。如果支持,那么支持Realm的getAuthenticationInfo方法将会伴随着提交的token被调用。getAuthenticationInfo方法有效地代表一个特定Realm的单一的身份验证尝试。

授权

Shiro简介及与spring集成

Shiro简介及与spring集成

Step 1:应用程序或框架代码调用任何Subject 的hasRole*, checkRole*, isPermitted*,或者checkPermission*方法的变体,传递任何所需的权限

Step 2:Subject的实例—通常是DelegatingSubject(或子类),调用securityManager的对应的方法。

Step 3:SecurityManager调用org.apache.shiro.authz.Authorizer接口的对应方法。默认情况下,authorizer 实例是一个ModularRealmAuthorizer实例,它支持协调任何授权操作过程中的一个或多个Realm实例。

Step 4:每个配置好的Realm被检查是否实现了相同的Authorizer接口。如果是,Realm各自的hasRole*, checkRole*,isPermitted*,或checkPermission*方法将被调用。

注销

logout(注销):currentUser.logout;

调用logout方法时,现有Session将失效,而且身份将失去关联(在Web应用程序中,RememberMe cookie将被删除)。

在Subject注销后,该Subject的实例被再次认为是匿名的。

注意:WEB应用程序记住身份往往依靠Cookie,然而Cookie只能在Response被返回后被删除,所以建议在调用subject.logout后立即向终端重定向一个新的视图或页面。这样即能保证与安全相关的Cookie都能像预期的一样被删除。

Shiro中默认的过滤器

Shiro简介及与spring集成

基于注解

Shiro提供的注解

@RequiresAuthentication :要求当前Subject已经在当前的session中被验证通过才能被注解的类/实例/方法访问或调用。

@RequiresGuest :要求当前的Subject是一个“guest”,也就是他们必须是在之前的session中没有被验证或记住才能被注解的类/实例/方法访问或调用。

@RequiresPermissions:要求当前的Subject被允许一个或多个权限,以便执行注解的方法,比如:@RequiresPermissions("account:create")

@RequiresRoles:要求当前的Subject拥有所有指定的角色。如果他们没有,则该方法将不会被执行,而且AuthorizationException异常将会被抛出。比如:@RequiresRoles("administrator")

@RequiresUser:需要当前的Subject是一个应用程序用户才能被注解的类/实例/方法访问或调用。要么是通过验证被确认,或者在之前session中的‘RememberMe’服务被记住。

基于标签

需要在jsp页面中倒入shiro 标签

guest 标签

guest标签将显示它包含的内容,仅当当前的Subject 被认为是guest时。

guest是指没有身份ID的任何Subject:没有登录也没有在上一次的访问中被记住(RememberMe 服务)

guest标签与user标签逻辑相反。

Hi there! Please Loginor

Signuptoday!

user标签

user标签将显示它包含的内容,仅当当前的Subject 被认为是user时。

user在上下文中被定义为一个已知身份ID的Subject,或是成功通过身份验证及通过RememberMe服务的。

该标签在语义上与authenticated 标签是不同的,authenticated标签更为严格。

User标签与guest标签逻辑相反。

authenticated 标签

当当前用户在当前会话中成功地通过了身份验证authenticated标签才会显示包含的内容。比user标签更为严格。在逻辑上与notAuthenticated标签相反。

Update your contact information.

notAuthenticated标签

当前Subject还没有在其当前会话中成功地通过验证,将会显示它所包含的内容

Please loginin order to update your credit card information.

principal标签

标签将会输出Subject的主体(标识属性)或主要的属性

Hello, , how are you today?

Typed principal

User ID:

Principal property

Hello,

hasRole标签

当前Subject被分配了具体的角色时显示它所包含的内容。hasRole标签与lacksRole标签逻辑相反。

Administer the system

lacksRole标签

Sorry, you are not allowed to administer the system.

hasAnyRole标签

当前的Subject被分配了任意一个来自于逗号分隔的角色名列表中的具体角色,将会显示它所包含的内容。

You are either a developer, project manager, or administrator.

hasPermission标签

当前Subject拥有特定的权限时,会显示它所包含的内容。hasPermission标签与lacksPermission标签逻辑相反。

Create a new User

lacksPermission标签

当前Subject没有拥有(蕴含)特定的权限,将会显示它所包含的内容。也就是说,用户没有特定的能力。

Sorry, you are not allowed to delete user accounts.

3、Spring集成Shiro

配置自定义Realm:实现自定义认证和授权

配置Shiro实体类使用的缓存策略

配置SecurityManager

配置保证Shiro内部Bean声明周期都得到执行的Lifecycle Bean后置处理器

配置AOP式方法级权限检查

配置Shiro Filter

基于Maven构建项目

Shiro简介及与spring集成

web.xml

xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee

http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"

version="3.1" metadata-complete="true">

applicationContext.xml

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"

xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

applicationContext-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">

/** = anon

springDispatherServlet-servlet.xml

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xmlns:context="http://www.springframework.org/schema/context"

xmlns:mvc="http://www.springframework.org/schema/mvc"

xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">

代码实现

自定义Realm

@Component("myRealm")

public class UserRealm extends AuthorizingRealm {

@Autowired

private UserService userService;

/**

* 授权方法,在配有缓存的情况下,只加载一次。

*/

protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

ShiroUser shiroUser = (ShiroUser) principals.getPrimaryPrincipal;

List

Set

for (Authority authority : authorities){

roles.add(authority.getName);

}

SimpleAuthorizationInfo info = new SimpleAuthorizationInfo;

info.setRoles(roles);

return info;

* 登录认证

* @param token

* @return

* @throws AuthenticationException

*/

@Transactional

protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

UsernamePasswordToken uptoken = (UsernamePasswordToken) token;

String username = uptoken.getUsername;

User user = userService.findUserByLoginName(username);

if( user == null ) {

throw new UnknownAccountException("用户名"+username+"不存在.");

}

if (user.getEnabled != 1){

throw new LockedAccountException(user.getName+"被锁定");

}

ShiroUser principal = new ShiroUser;

String userInfo = user.getName+"("+user.getRole.getName+")";

principal.setUserInfo(userInfo);

principal.setAuthorities(user.getRole.getAuthorities);

Object hashedCredentials = user.getPassword;

ByteSource credentialsSalt = ByteSource.Util.bytes(user.getSalt);

String realmName = getName;

SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal,hashedCredentials, credentialsSalt,realmName);

public class ShiroUser{

//userName(roleName)

private String userInfo;

private List

public List

return authorities;

}

public void setAuthorities(List

this.authorities = authorities;

}

public String getUserInfo {

return userInfo;

}

public void setUserInfo(String userInfo) {

this.userInfo = userInfo;

* 初始化 使用MD5加密

*/

@PostConstruct

public void initCredentialsMatcher{

HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher;

hashedCredentialsMatcher.setHashAlgorithmName("MD5");

// 加密1024次

hashedCredentialsMatcher.setHashIterations(1024);

//

this.setCredentialsMatcher(hashedCredentialsMatcher);

Main方法生成salt 和password

// 利用shiro提供的随机数生成类、生成随机数,用于盐值加密

SecureRandomNumberGenerator secureRandomNumberGenerator =

new SecureRandomNumberGenerator;

String str = secureRandomNumberGenerator.nextBytes.toBase64;

System.out.println(str);//

String algorithmName = "MD5";

Object source = "123456";

//盐值 NWJtcFg3VFNtNEdEWTh6UUtoSTRkdz09

ByteSource salt = ByteSource.Util.bytes(str);

int hashIterations = 1024;

Object result = new SimpleHash(algorithmName,source,salt,hashIterations);

System.out.println(result);//数据库中密码 b9aa43cfb15649c932ac647df54101a7

LoginController.java

@RequestMapping(value = "/login",method = RequestMethod.POST)

private String login(HttpServletRequest request,Modelmodel,

@RequestParam(name = "username",required = false) String username, @RequestParam(name = "password",required = false) String password, RedirectAttributes attributes){

UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username, password);

//usernamePasswordToken.setRememberMe(false);

Subject subject = SecurityUtils.getSubject;

String message = null;

if(!subject.isAuthenticated){

try {

subject.login(usernamePasswordToken);

} catch (UnknownAccountException ex) {//用户名没有找到

//ex.printStackTrace;

message = "用户名不存在";

} catch (IncorrectCredentialsException ex) {//用户名密码不匹配

// ex.printStackTrace;

message = "用户名和密码不匹配";

}catch (LockedAccountException lae) {// 用户被锁定

//lae.printStackTrace;

message = "用户被锁定";

}catch (AuthenticationException e) {//其他的登录错误

e.printStackTrace;

}

if(message!=null){

attributes.addFlashAttribute("message", message);

return "redirect:/shiro-login";

return "redirect:/success";

}

基于注解的权限设置

在controller list方法上加上 @RequiresRoles(value={"admin"}) 说明此方法需要有admin角色的用户才可以操作。

@RequiresRoles(value={"admin"})

@RequestMapping(value = "/list", method= RequestMethod.GET)

public String list(Model model, HttpServletRequest request){

List

model.addAttribute("list", userList);

return "user/userList";

}

我们用两个用户来测试

Shiro简介及与spring集成

Shiro简介及与spring集成

可以看到admin用户可以访问list页面,而gx因为没有admin的角色,访问失败。

附:数据库关系图

Shiro简介及与spring集成

总结:本文通过例子,简单实现了权限的控制,相比与spring secruity的复杂配置,shiro更加简单。

例子github:https://github.com/zhaoguoxin/gxshiro.git

1 1
原创粉丝点击