03-2 shiro身份验证
在shiro中用户需要提供身份(principals)和证明(credentials)给shiro。从而应用可以验证用户身份。 principals:身份,主体的标识属性(用户名) credentials:证明/凭证,只有主体知道的安全值(密码) Subject:主体 Realm:数据源
本篇内容:
1、通过ini身份验证 2、身份验证流程 3、使用realm身份验证 4、使用多个realm身份验证 5、使用数据库身份验证
本篇demo项目结构
01-check
|---01-check-demo
|---02-check-realm
|---03-check-realm-more
|---04-check-jdbc
一、环境搭建
1、添加pom依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.2</version>
</dependency>
</dependencies>
二、简单使用身份验证:
2.1、创建项目:01-check-demo
2.2、创建用户身份和凭证文件:shiro.ini
[users]
zhang=123
wang=123
2.3、身份校验
package com.check;
import org.apache.shiro.SecurityUtils;
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;
import org.junit.Test;
/**
* @author brusion
* @date 2018/9/15
*/
public class ShiroApplication {
@Test
public void shiroTest() {
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager manager = factory.getInstance();
SecurityUtils.setSecurityManager(manager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");
try {
subject.login(token);
System.out.println("=== 登录成功 === ");
} catch (Exception e) {
System.out.println("=== 登录失败 === ");
}
subject.logout();
}
}
2.4、测试结果
--- 登录成功 ---
2.5、API说明
2.5.1、Factory工厂类
Factory
|---JndiObjectFactory
|---AbstractFactory
|---IniFactorySupport
|---IniSecurityManagerFactory
2.5.2、SecurityManager接口对象
interface SecurityManager extends Authenticator, Authorizer, SessionManager {
Subject login(Subject var1, AuthenticationToken var2) throws AuthenticationException;
void logout(Subject var1);
Subject createSubject(SubjectContext var1);
}
2.5.3、SecurityUtils工具类
public abstract class SecurityUtils {
private static SecurityManager securityManager;
public SecurityUtils() ;
public static Subject getSubject();
public static void setSecurityManager(SecurityManager securityManager);
public static SecurityManager getSecurityManager();
}
2.5.4、UsernamePasswordToken
Serializable
|---AuthenticationToken
|---HostAuthenticationToken
|---UsernamePasswordToken
|---RememberMeAuthenticationToken
|---UsernamePasswordToken
三、shiro的身份认证流程
1、调用Subject#login(token)进行登录,会自动委托给Security Manager。(调用前需要设置SecurityManager) 2、SecurityManager负责身份验证逻辑,并委托给Authenticator进行身份验证(Authenticator是真正的身份验证者) 3、Authenticator会委托给相应的AuthenticationStrategy进行多Realm身份验证。(默认ModularRealmAuthenticator会调用AuthenticatorStrategy进行Realm身份验证) 4、Authenticator会把相应的token传入Realm,从Realm获取身份验证信息。如果没有对应信息则抛出异常。
四、使用realm作为数据源
1、创建自定义Realm
理解为将用户的账号和密码没有用ini文件的方式,而是采用自定义realm方式配置数据
package com.check;
import org.apache.shiro.authc.*;
import org.apache.shiro.realm.Realm;
/**
* @author brusion
* @date 2018/9/15
*/
public class SingleRealm implements Realm {
@Override
public String getName() {
return getClass().getName();
}
@Override
public boolean supports(AuthenticationToken token) {
//只支持UsernamePasswordToken类型的token
return token instanceof UsernamePasswordToken;
}
@Override
public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//获取用户名和密码
String name = (String) token.getPrincipal();
String pwd = new String((char[]) token.getCredentials());
if (!"zhang".equals(name)) {
throw new UnknownAccountException("账号不存在..");
}
if (!"123".equals(pwd)) {
throw new IncorrectCredentialsException("密码错误...");
}
//身份校验成功返回AuthenticationInfo实现类
return new SimpleAuthenticationInfo(name, pwd, getName());
}
}
2、创建配置文件:shiro_realm.ini
myRealm=com.check.SingleRealm
securityManager.realms=$myRealm
说明:
myRealm=com.check.SingleRealm:声明自定义realm securityManager.realms=$myRealm:指定SecurityManager的Realm实现类
3、身份校验
package com.check;
import org.apache.shiro.SecurityUtils;
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;
import org.junit.Test;
/**
* @author brusion
* @date 2018/9/15
*/
public class RealmApplication {
@Test
public void realmTest(){
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro_realm.ini");
SecurityManager manager = factory.getInstance();
SecurityUtils.setSecurityManager(manager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("zhang","123");
try {
subject.login(token);
System.out.println("=== 登录成功 === ");
} catch (Exception e) {
System.out.println("=== 登录失败 === ");
}
subject.logout();
}
}
4、测试结果
=== 登录成功 ===
五、多个realm方式数据配置
1、创建项目:03-check-realm-more
2、创建自定义Realm类 realm1
package com.check.realm;
import org.apache.shiro.authc.*;
import org.apache.shiro.realm.Realm;
/**
* @author brusion
* @date 2018/9/15
*/
public class Realm1 implements Realm {
@Override
public String getName() {
return getClass().getName();
}
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof UsernamePasswordToken;
}
@Override
public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String name = (String) token.getPrincipal();
String pwd = new String((char[]) token.getCredentials());
if (!"zhang".equals(name)) {
throw new UnknownAccountException("账号错误");
}
if (!"123".equals(pwd)) {
throw new IncorrectCredentialsException("密码错误");
}
return new SimpleAuthenticationInfo(name, pwd, getName());
}
}
3、自定义realm类realm2
package com.check.realm;
import org.apache.shiro.authc.*;
import org.apache.shiro.realm.Realm;
/**
* @author brusion
* @date 2018/9/15
*/
public class Realm2 implements Realm {
@Override
public String getName() {
return getClass().getName();
}
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof UsernamePasswordToken;
}
@Override
public AuthenticationInfo getAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String name = (String) token.getPrincipal();
String pwd = new String((char[]) token.getCredentials());
if (!"san".equals(name)) {
throw new UnknownAccountException("账号错误");
}
if (!"456".equals(pwd)) {
throw new IncorrectCredentialsException("密码错误");
}
return new SimpleAuthenticationInfo(name, pwd, getName());
}
}
4、创建配置文件:shiro_more_realm.ini
myRealm1= com.check.realm.Realm1
myRealm2= com.check.realm.Realm2
securityManager.realms=$myRealm1,$myRealm2
5、验证身份
package com.check;
import org.apache.shiro.SecurityUtils;
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;
import org.junit.Test;
/**
* 多个realm数据源
*
* @author brusion
* @date 2018/9/15
*/
public class MoreRealmApplication {
@Test
public void moreRealmTest() {
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro_more_realm.ini");
SecurityManager manager = factory.getInstance();
SecurityUtils.setSecurityManager(manager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("san", "456");
try {
subject.login(token);
System.out.println("== 登录成功 ===");
} catch (Exception e) {
System.out.println("== 登录失败 ===");
}
subject.logout();
}
}
6、测试运行
== 登录成功 ===
六、从数据库中获取数据源
前面几个demo中的数据都是从ini文件或者realm中获取,实际项目中数据都是从数据库中获取 本案例采用jdbc方式连接获取数据库中数据并进行数据校验
1、数据库表创建
drop database if exists shiro;
create database shiro;
use shiro;
create table users (
id bigint auto_increment,
username varchar(100),
password varchar(100),
password_salt varchar(100),
constraint pk_users primary key(id)
) charset=utf8 ENGINE=InnoDB;
create unique index idx_users_username on users(username);
create table user_roles(
id bigint auto_increment,
username varchar(100),
role_name varchar(100),
constraint pk_user_roles primary key(id)
) charset=utf8 ENGINE=InnoDB;
create unique index idx_user_roles on user_roles(username, role_name);
create table roles_permissions(
id bigint auto_increment,
role_name varchar(100),
permission varchar(100),
constraint pk_roles_permissions primary key(id)
) charset=utf8 ENGINE=InnoDB;
create unique index idx_roles_permissions on roles_permissions(role_name, permission);
insert into users(username,password)values('zhang','123');
2、项目创建:04-check-jdbc
3、pom中添加数据库连接库配置
<dependencies>
<!--数据库连接池-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.25</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>0.2.23</version>
</dependency>
</dependencies>
4、创建配置文件
jdbcRealm=org.apache.shiro.realm.jdbc.JdbcRealm
dataSource=com.alibaba.druid.pool.DruidDataSource
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql://localhost:3306/shiro
dataSource.username=root
dataSource.password=123123
jdbcRealm.dataSource=$dataSource
securityManager.realms=$jdbcRealm
5、身份验证
package com.check;
import org.apache.shiro.SecurityUtils;
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;
import org.junit.Test;
/**
* @author brusion
* @date 2018/9/15
*/
public class JDBCApplication {
@Test
public void jdbcTest(){
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro_jdbc_realm.ini");
SecurityManager manager = factory.getInstance();
SecurityUtils.setSecurityManager(manager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("zhang", "123");
try {
subject.login(token);
System.out.println("== 登录成功 ===");
} catch (Exception e) {
System.out.println("== 登录失败 ===");
}
subject.logout();
}
}
6、运行测试
== 登录成功 ===