Shiro学习配置
简介:Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
shiro实现认证
Shiro.ini
#用户
[users]
#用户zhang的密码是123,此用户具有role1和role2两个角色
zhangsan=123,role1,role2
wangwu2=123,role1,role3
wangwu3=123,role1
wangwu4=123,role1,role2
#权限
[roles]
#角色role1对资源user拥有create、update权限
role1=user:create,user:update
#角色role2对资源user拥有create、delete权限
role2=user:create,user:delete
#角色role3对资源user拥有create权限
role3=user:create
#全部匹配(超级用户)
role4=*:*
#此方法用于没有设置defaultSecurityManager.setRealm(userRealm);自定义realm
[main]
#创建useRealm对象
useRealm=com.test.shiro.UserRealm
#将当前对象传给安全管理器
SecurityManager=org.apache.shiro.mgt.DefaultSecurityManager
SecurityManager.realm=$userRealm
log4j
log4j.rootLogger=INFO,consoleAppender,logfile,MAIL
log4j.addivity.org.apache=true
#ConsoleAppender,控制台输出
#FileAppender,文件日志输出
#SMTPAppender,发邮件输出日志
#SocketAppender,Socket 日志
#NTEventLogAppender,Window NT 日志
#SyslogAppender,
#JMSAppender,
#AsyncAppender,
#NullAppender
#文件输出:RollingFileAppender
#log4j.rootLogger = INFO,logfile
log4j.appender.logfile = org.apache.log4j.RollingFileAppender
log4j.appender.logfile.Threshold = INFO
# 输出以上的 INFO 信息
log4j.appender.logfile.File = INFO_log.html
#保存 log 文件路径
#Log4j 从入门到详解
10
log4j.appender.logfile.Append = true
# 默认为 true,添加到末尾,false 在每次启动时进行覆盖
log4j.appender.logfile.MaxFileSize = 1MB
# 一个 log 文件的大小,超过这个大小就又会生成 1 个日志 # KB ,MB,GB
log4j.appender.logfile.MaxBackupIndex = 3
# 最多保存 3 个文件备份
log4j.appender.logfile.layout = org.apache.log4j.HTMLLayout
# 输出文件的格式
log4j.appender.logfile.layout.LocationInfo = true
#是否显示类名和行数
log4j.appender.logfile.layout.Title=title:提醒您:系统发生了严重错误
#html 页面的 < title >
############################## SampleLayout ####################################
# log4j.appender.logfile.layout = org.apache.log4j.SampleLayout
############################## PatternLayout ###################################
# log4j.appender.logfile.layout = org.apache.log4j.PatternLayout
# log4j.appender.logfile.layout.ConversionPattern =% d % p [ % c] - % m % n % d
############################## XMLLayout #######################################
# log4j.appender.logfile.layout = org.apache.log4j.XMLLayout
# log4j.appender.logfile.layout.LocationInfo = true #是否显示类名和行数
############################## TTCCLayout ######################################
# log4j.appender.logfile.layout = org.apache.log4j.TTCCLayout
# log4j.appender.logfile.layout.DateFormat = ISO8601
#NULL, RELATIVE, ABSOLUTE, DATE or ISO8601.
# log4j.appender.logfile.layout.TimeZoneID = GMT - 8 : 00
# log4j.appender.logfile.layout.CategoryPrefixing = false ##默认为 true 打印类别名
# log4j.appender.logfile.layout.ContextPrinting = false ##默认为 true 打印上下文信息
# log4j.appender.logfile.layout.ThreadPrinting = false ##默认为 true 打印线程名
# 打印信息如下:
#2007 - 09 - 13 14 : 45 : 39 , 765 [http - 8080 - 1 ] ERROR com.poxool.test.test -
#error 成功关闭链接
###############################################################################
#每天文件的输出:DailyRollingFileAppender
#log4j.rootLogger = INFO,errorlogfile
log4j.appender.errorlogfile = org.apache.log4j.DailyRollingFileAppender
log4j.appender.errorlogfile.Threshold = ERROR
log4j.appender.errorlogfile.File = ../logs/ERROR_log
log4j.appender.errorlogfile.Append = true
#默认为 true,添加到末尾,false 在每次启动时进行覆盖
log4j.appender.errorlogfile.ImmediateFlush = true
#直接输出,不进行缓存
# ' . ' yyyy - MM: 每个月更新一个 log 日志
# ' . ' yyyy - ww: 每个星期更新一个 log 日志
# ' . ' yyyy - MM - dd: 每天更新一个 log 日志
# ' . ' yyyy - MM - dd - a: 每天的午夜和正午更新一个 log 日志
# ' . ' yyyy - MM - dd - HH: 每小时更新一个 log 日志
# ' . ' yyyy - MM - dd - HH - mm: 每分钟更新一个 log 日志
#Log4j 从入门到详解
#11
log4j.appender.errorlogfile.DatePattern = ' . ' yyyy - MM - dd ' .log '
#文件名称的格式
log4j.appender.errorlogfile.layout = org.apache.log4j.PatternLayout
log4j.appender.errorlogfile.layout.ConversionPattern =%d %p [ %c] - %m %n %d
#控制台输出:
#log4j.rootLogger = INFO,consoleAppender
log4j.appender.consoleAppender = org.apache.log4j.ConsoleAppender
log4j.appender.consoleAppender.Threshold = ERROR
log4j.appender.consoleAppender.layout = org.apache.log4j.PatternLayout
log4j.appender.consoleAppender.layout.ConversionPattern =%d %-5p %m %n
log4j.appender.consoleAppender.ImmediateFlush = true
# 直接输出,不进行缓存
log4j.appender.consoleAppender.Target = System.err
# 默认是 System.out 方式输出
#发送邮件:SMTPAppender
#log4j.rootLogger = INFO,MAIL
log4j.appender.MAIL = org.apache.log4j.net.SMTPAppender
log4j.appender.MAIL.Threshold = INFO
log4j.appender.MAIL.BufferSize = 10
log4j.appender.MAIL.From = yourmail@gmail.com
log4j.appender.MAIL.SMTPHost = smtp.gmail.com
log4j.appender.MAIL.Subject = Log4J Message
log4j.appender.MAIL.To = yourmail@gmail.com
log4j.appender.MAIL.layout = org.apache.log4j.PatternLayout
log4j.appender.MAIL.layout.ConversionPattern =%d - %c -%-4r [%t] %-5p %c %x - %m %n
#数据库:JDBCAppender
log4j.appender.DATABASE = org.apache.log4j.jdbc.JDBCAppender
log4j.appender.DATABASE.URL = jdbc:oracle:thin:@ 210.51 . 173.94 : 1521 :YDB
log4j.appender.DATABASE.driver = oracle.jdbc.driver.OracleDriver
log4j.appender.DATABASE.user = ydbuser
log4j.appender.DATABASE.password = ydbuser
log4j.appender.DATABASE.sql = INSERT INTO A1 (TITLE3) VALUES ( ' %d - %c %-5p %c %x - %m%n' )
log4j.appender.DATABASE.layout = org.apache.log4j.PatternLayout
log4j.appender.DATABASE.layout.ConversionPattern =% d - % c -%- 4r [ % t] %- 5p % c %x - % m % n
#数据库的链接会有问题,可以重写 org.apache.log4j.jdbc.JDBCAppender 的 getConnection() 使用数
#据库链接池去得链接,可以避免 insert 一条就链接一次数据库
Test:
public class test2 {
private static final transient Logger log = LoggerFactory.getLogger(test2.class);
public static void main(String[] args) {
String name="zhangsan";
String password = "123";
// 构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
//继承 TextConfigurationRealm,获取自定义shiro资源文件里的信息
IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
//因为最终是使用Realm来验证,所以要设置Realm
defaultSecurityManager.setRealm(iniRealm);
// /先设置认证环境
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
//接下来就是登陆了,如果传入的用户名不正确,和Realm中的不匹配,那么久会异常
AuthenticationToken token = new UsernamePasswordToken(name,password);
try {
subject.login(token);
System.out.println("登陆成功");
}catch ( AuthenticationException uae){
System.out.println("登录失败");
}
}
shiro实现授权
授权在认证之后进行;
1,授权概述摆权,也叫访问控制,即在应用中控利谁能访问这些资源(如访问页面/编辑数据/页面操作等)在授权中需了解几个关健对象;主体 (subject)资源 (resource)、权限 (Permission)角色(Role).
2,关键对象介绍1.主体
主体,即访问应用的用户,在Shiro中使用Subject代表该用户,用户只有授权后才允许访问相应的资源。
2,资源在应用中,用户可以访问的任何东西,比如访问可JSP负面、查看/编辑某些数据、访问某个业务方法、打印文本等等部是资源,用户只要授权后才能访问。
3,权限
安全策略中的原子授权单位,通过权限我们可以表示在应用中用户有没有操作某个资源的权力。即权限表示在应用中用户能不能访问某个资源,如:访问用户列表页面查看/新增/修改/ 删除用户数据(即很多时候部是CRUD 式权限控利)打印文裆等等,
4,角色
角色代表了操作集合,可以理解为权限的集合,一股情况下我们会赋予用户角色而不是权限,赋予权限时比较方便,典型的如:项目理理、技术总监、CTO开发工程师等都是角色,不可的角色拥有一组不同的权限。
package com.test.shiro;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
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;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.List;
/**
* @auther:
* @creater:
* @time
**/
public class test2 {
private static final transient Logger log = LoggerFactory.getLogger(test2.class);
public static void main(String[] args) {
String name="zhangsan";
String password = "123";
// 构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
//继承 TextConfigurationRealm,获取自定义shiro资源文件里的信息
IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
//因为最终式使用Realm来验证,所以要设置Realm
defaultSecurityManager.setRealm(iniRealm);
// /先设置认证环境
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
//接下来就是登陆了,如果传入的用户名不正确,和Realm中的不匹配,那么久会异常
AuthenticationToken token = new UsernamePasswordToken(name,password);
try {
subject.login(token);
System.out.println("登陆成功");
}catch ( AuthenticationException uae){
System.out.println("登录失败");
}
// subject.logout();注销登录
// 判断用户是否认证通过
boolean authenticated = subject.isAuthenticated();
System.out.println("是否认证通过:"+authenticated);
// 角色判断
boolean role1 = subject.hasRole("role1");
System.out.println("是否有该角色:"+role1);
// 判断集合里面的角色,返回数组
List<String> list = Arrays.asList("role1","role2","role3");
boolean[] b = subject.hasRoles(list);
for (boolean b1 : b) {
System.out.println("是否有该角色(数组):"+b1);
}
// 判断当前用户是否有list里的角色
boolean b1 = subject.hasAllRoles(list);
System.out.println("是否有所有权限"+b1);
// 权限判断
boolean permitted = subject.isPermitted("user:update");
System.out.println("是否有权限"+permitted);
boolean[] permitted2 = subject.isPermitted("user:update","user:create","user:delete");
System.out.println("是否有权限"+permitted2);
boolean permitted3 = subject.isPermittedAll("user:update","user:create","user:delete");
System.out.println("是否有权限"+permitted3);
}
}
自定义Realm实现认证
shiro使用默认Realm,即配置文件ini的数据,大部分需要从系统的数据库里读取数据,所以要自定义Realm;
最基础的是Realm接口,CachingRealm负责缓存处理,AuthenticationRealm负责认证,AuthgrizingRealm负责授权,通常自定义的realm继承AuthorizingRealn
认证只调用一次,授权调用多次(所以将查询放在认证处)
详细代码如下;
bean
User:(放实体类)
public class User {
private Integer id;
private String username;
private String pwd;
public User() {
}
public User(Integer id, String username, String pwd) {
this.id = id;
this.username = username;
this.pwd = pwd;
}
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 getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
Activer:(放user,role,permission,为了传递参数用)
public class ActierUser {
private User user;
private List<String> role;
private List<String> permission;
public ActierUser(User user, List<String> role, List<String> permission) {
this.user = user;
this.role = role;
this.permission = permission;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public List<String> getRole() {
return role;
}
public void setRole(List<String> role) {
this.role = role;
}
public List<String> getPermission() {
return permission;
}
public void setPermission(List<String> permission) {
this.permission = permission;
}
}
service
public interface PermissionService {
List<String> queryPerm(String name);
}
public interface RoleService {
List<String> queryRole(String name);
}
public interface UserService {
// 查询对象
User query(String username);
}
Impl
public class PermissionServiceImpl implements PermissionService
{
@Override
public List<String> queryPerm(String username) {
List<String> list = Arrays.asList("user:create","user:update");
return list;
}
}
public class RoleServiceImpl implements RoleService {
@Override
public List<String> queryRole(String name) {
List<String> list = Arrays.asList("role1","role2","role3");
return list;
}
}
public class UserServiceImpl implements UserService {
@Override
public User query(String name) {
User user=null;
switch (name)
{
case "zhangsan":
user = new User(1,"zhangsan","123");
break;
default:
break;
}
return user;
}
}
自定义realm
package com.test.ceshi;
import com.sun.xml.internal.bind.v2.model.core.ID;
import com.test.bean.ActierUser;
import com.test.bean.User;
import com.test.service.PermissionService;
import com.test.service.RoleService;
import com.test.serviceimpl.PermissionServiceImpl;
import com.test.serviceimpl.RoleServiceImpl;
import com.test.serviceimpl.UserServiceImpl;
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.AuthenticatingRealm;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import java.util.ArrayList;
import java.util.Collection;
/**
* @auther:
* @creater:
* @time
**/
//AuthenticatingRealm做认证的方法
//AuthorizingRealm既可以认证也可以授权,不过要记住继承以下接口
public class UserRealm extends AuthorizingRealm {
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//token.getPrincipal()从test03里里获取身份信息;AuthenticationToken:身份验证令牌
String username = token.getPrincipal().toString();//从表头
System.out.println(username);
/*
以前用户名密码一起拿到数据库进行匹配,在shiro里根据用户名把用户对象查询出来,再做密码匹配
*/
UserServiceImpl us = new UserServiceImpl();
RoleServiceImpl roleService = new RoleServiceImpl();
PermissionServiceImpl permissionService = new PermissionServiceImpl();
User user = us.query(username);
//创建一个对象,这个对象可以放user,role,per,这样每个只查询一次就可以得到结果,将ActierUser利用PrincipalCollection获取到
ActierUser actierUser = new ActierUser(user, roleService.queryRole(username), permissionService.queryPerm(username));
if (user != null) {
//第一个参数可以传任意对象 从数据库获得的密码 从数据库得到的名字
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(actierUser, user.getPwd(), user.getUsername());
return info;
} else {
return null;
}
}
/*
授权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
ActierUser user = (ActierUser) principals.getPrimaryPrincipal();//PrincipalCollection 身份认证后得到的验证后的集合
//这里是 ActierUser 对象;
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//角色
Collection<String> roles = user.getRole();
if (roles != null && roles.size() > 0) {
info.addRoles(roles);
}
//权限
Collection<String> permissions = user.getPermission();
if (permissions != null && permissions.size() > 0) {
info.addStringPermissions(permissions);
}
// if (user.getUser().getType()==0)超级管理员设置方法,getType()管理员种类,数据库设计表时候有
// {
// info.addStringPermission("*:*");
// }
return info;
}
}
Test
public class test06 {
private static final transient Logger log = LoggerFactory.getLogger(test06.class);
public static void main(String[] args) {
String name="zhangsan";
String password = "123";
// 构建SecurityManager环境
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
//继承 TextConfigurationRealm,获取自定义shiro资源文件里的信息
// IniRealm iniRealm = new IniRealm("classpath:shiro.ini");
//因为最终式使用Realm来验证,所以要设置Realm
// 创建realm
UserRealm userRealm = new UserRealm();
// 注入userRealm
defaultSecurityManager.setRealm(userRealm);
// /先设置认证环境
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
//接下来就是登陆了,如果传入的用户名不正确,和Realm中的不匹配,那么久会异常
AuthenticationToken token = new UsernamePasswordToken(name,password);
try {
subject.login(token);
System.out.println("登陆成功");
String s = subject.getPrincipal().toString();
System.out.println(s);
}catch ( AuthenticationException uae){
System.out.println("登录失败");
}
// subject.logout();注销登录
// 判断用户是否认证通过
boolean authenticated = subject.isAuthenticated();
System.out.println("是否认证通过:"+authenticated);
// 角色判断
boolean role1 = subject.hasRole("role1");
System.out.println("是否有该角色:"+role1);
// 判断集合里面的角色,返回数组
List<String> list = Arrays.asList("role1","role2","role3");
boolean[] b = subject.hasRoles(list);
for (boolean b1 : b) {
System.out.println("是否有该角色(数组):"+b1);
}
// 判断当前用户是否有list里的角色
boolean b1 = subject.hasAllRoles(list);
System.out.println("是否有所有权限"+b1);
// 权限判断
boolean permitted = subject.isPermitted("user:update");
System.out.println("是否有权限"+permitted);
boolean[] permitted2 = subject.isPermitted("user:update","user:create","user:delete");
System.out.println("是否有权限"+permitted2);
boolean permitted3 = subject.isPermittedAll("user:update","user:create","user:delete");
System.out.println("是否有权限"+permitted3);
}
}