Shiro
shiro介绍: Shiro是一个强大且易用的Java安全框架,有身份验证、授权、密码学和会话管理
Spring security 重量级安全框架(配置很麻烦 做的比较细)
Apache Shiro轻量级安全框架 (配置很容易 很方便,很容易使用)
shiro使用的地方:用户登录和登录权限验证
shiro入门:
导包:
准备资源文件:shiro.ini文件
[users]
# 用户 'root' 密码是 'secret' and the 'admin' 角色
root = secret, admin
# 用户 'guest' 密码 'guest' 和'guest'角色
guest = guest, guest
# user 'presidentskroob' with password '12345' ("That's the same combination on
# my luggage!!!" ;)), and role 'president'
presidentskroob = 12345, president
# 用户 'darkhelmet' with password 'ludicrousspeed' and 角色 'darklord' and 'schwartz'
darkhelmet = 123, darklord, schwartz
# 用户 'lonestarr' 密码 'vespa' and roles 'goodguy' and 'schwartz'
lonestarr = vespa, goodguy, schwartz
[roles]
# 'admin' role has all permissions, indicated by the wildcard '*'
admin = *
# 这个 'schwartz' 角色 能干lightsaber下面的所有事情:
schwartz = lightsaber:*
# goodguy这个角色 能干winnebago下面的drive权限,操作eagle5这个资源 user:delete:5
goodguy = winnebago:drive:eagle5
测试登录验证的权限:
//读取到shiro.ini文件,并且根据文件创建SecurityManager对象
// SecurityManager:权限的核心管理对象
Factory
SecurityManager securityManager = factory.getInstance();
// 为了程序正常运行,需要设计一个这个securityManager(相当于把这个管理放到环境中去了)
SecurityUtils.setSecurityManager(securityManager);
//拿到当前用户【Subject:操作当前系统的一个用户】
Subject currentUser = SecurityUtils.getSubject();
//拿到一个会话(它在咱们的所有类型系统都可以使用【不要求必需是web项目】)
Session session = currentUser.getSession();
//会话操作【可以在会话中设置值与获取值】
session.setAttribute("someKey", "123456");
String value = (String) session.getAttribute("someKey");
if (value.equals("123456")) {
System.out.println("可以获取到这个值"+value);
}
//判断这个用户是否经过验证(没有登录我们可以认为是一个游客)
//没有登录,我们就可以完成登录
if (!currentUser.isAuthenticated()) {
//拿到用户名与密码令牌【拿到用户名与密码】
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
//设置记住我的功能
token.setRememberMe(true);
try {
//完成登录功能
currentUser.login(token);
} catch (UnknownAccountException uae) {
//log.info("There is no user with username of " + token.getPrincipal());
//token.getPrincipal()[ˈprɪnsəpl] -> 拿到主体(登录用户的主要标识【用户名】)
System.out.println("这没有这个账号(用户名)"+token.getPrincipal());
} catch (IncorrectCredentialsException ice) {
//incorrect [ˌɪnkəˈrekt]:不正确的,错误的
//Credentials[krəˈdenʃlz]:证书; 凭证,证件
//log.info("Password for account " + token.getPrincipal() + " was incorrect!");
System.out.println("这个密码是错误的"+token.getPrincipal());
} catch (LockedAccountException lae) {
System.out.println("这个账号已经被锁定");
}
//其它的登录失败异常【它是所有登录异常的父类】
catch (AuthenticationException ae) {
//unexpected condition? error?
}
}
//用户登录成功
log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
//判断这个用户是否是某一个角色
if (currentUser.hasRole("schwartz")) {
log.info("May the Schwartz be with you!");
} else {
log.info("Hello, mere mortal.");
}
//测试用户是否有某一个权限
if (currentUser.isPermitted("lightsaber:wield")) {
log.info("You may use a lightsaber ring. Use it wisely.");
} else {
log.info("Sorry, lightsaber rings are for schwartz masters only.");
}
//a (very powerful) Instance Level permission:
if (currentUser.isPermitted("winnebago:drive:eagle5")) {
log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " +
"Here are the keys - have fun!");
} else {
log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
}
//登出(注销当前登录用户)
currentUser.logout();
System.exit(0);//退出虚拟机
自定权限验证:
1.首先一个类去继承AuthorizingRealm类会实现两个方法
//权限验证方法
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
//用户名验证方法
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
return null;
}
用户验证测试//身份认证
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//得到令牌这个authenticationToken就是UsernamePasswordtoken
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
//或得用户主体
Object principal = token.getPrincipal();
//获取前台传送的进来的用户名这个用户名是传过来的
String username = token.getUsername();
//这里根据用户名去获取密码
String password = getByName(username);
if(password==null){
return null;
}
else{
//账号密码传过来与自己的数据库进行比对
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, password,null, getName());
return simpleAuthenticationInfo;
}
}
//这里根据用户名去获取密码
public String getByName(String username){
//后台的用户名和前台的进行对比如果过相等就返回123不等就返回null
if("haoge".equals(username)){
return "123";
}else{
return null;
}
}
//权限认证
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//拿到用户信息
String primaryPrincipal = (String)principalCollection.getPrimaryPrincipal();
//从数据库中拿到用户的权限
Set roles = getRoleByPrincipal(primaryPrincipal);
//从数据库中去拿到用户的角色信息
Set permissions = getPermissionByPrincipal(primaryPrincipal);
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
//设置获取角色
simpleAuthorizationInfo.setRoles(roles);
//设置获取的权限
simpleAuthorizationInfo.setStringPermissions(permissions);
//返回simpleAuthorizationInfo对象
return simpleAuthorizationInfo;
}
//模拟数据权限想信息
private Set getPermissionByPrincipal(String primaryPrincipal) {
Set permissions = new HashSet();
if("haoge".equals(primaryPrincipal)){
//如果账号密码正确,就返回全部权限
permissions.add("driver:*");
return permissions;
}else{
return null;
}
}
//模拟数据库角色信息
private Set getRoleByPrincipal(String primaryPrincipal) {
//创建一个集合
Set roles = new HashSet();
if("haoge".equals(primaryPrincipal)){
roles.add("admin");
return roles;
}else{
return null;
}
}
测试类:
@Test
public void test()throws Exception{
//获取自定义验证对象
MyRealm myRealm = new MyRealm();
//得到SecutiryManager 核心对象
DefaultSecurityManager securityManager = new DefaultSecurityManager();
//把自定义验证放进核心对象里面
securityManager.setRealm(myRealm);
//设置到shiro的环境里面 才能运行
SecurityUtils.setSecurityManager(securityManager);
//拿到游客对象
Subject currentUser = SecurityUtils.getSubject();
//创建令牌给出前台的数据
UsernamePasswordToken token = new UsernamePasswordToken("haoge","123","primaryPrincipal");
try {
//游客对象登录传数据进去
currentUser.login(token);
System.out.println("登陆成功");
} catch (UnknownAccountException e) {
e.printStackTrace();
System.out.println("账号不存在");
}catch (IncorrectCredentialsException e) {
e.printStackTrace();
System.out.println("密码出问题..");
}catch (AuthenticationException e) {
e.printStackTrace();
System.out.println("其他认证错误");
}
if(currentUser.hasRole("admin")){
System.out.println("这个用户有admin角色");
}else{
System.out.println("没有角色");
}
if ( currentUser.isPermitted("lightsaber:*")) {
System.out.println("该角色具有lightsaber这个权限");
}else{
System.out.println("没有权限");
}
currentUser.logout();//退出
}
密码加密功能:
@Test
public void testMyRealm() throws Exception{
//创建自己定义的Realm
MyRealm myRealm = new MyRealm();
//把Realm放到securityManager中去
DefaultSecurityManager securityManager = new DefaultSecurityManager();
securityManager.setRealm(myRealm);
//把权限管理器放到相应的环境中(我们可以在项目任何位置拿到)
SecurityUtils.setSecurityManager(securityManager);
//设置咱们Realm的密码匹配器(我们的密码要怎么处理)
HashedCredentialsMatcher matcher = new HashedCredentialsMatcher();
matcher.setHashAlgorithmName("md5"); //匹配器使用MD5的算法
matcher.setHashIterations(10);//加密算法要迭代多少次
myRealm.setCredentialsMatcher(matcher);
//拿到当前用户(Subject就是当前用户,游客)
Subject currentUser = SecurityUtils.getSubject();
//准备登录的令牌(准备用户名与密码) -> 这里的密码进行了加密
UsernamePasswordToken token = new UsernamePasswordToken("admin","123456");
try {
//根据令牌进行功能登录(当前用户进行登录)
currentUser.login(token);
System.out.println("登录成功啦。。。。");
} catch (UnknownAccountException e) {
System.out.println("这个账号不存在!" + token.getPrincipal());
e.printStackTrace();
} catch (IncorrectCredentialsException ice) {
System.out.println("这个密码不存在!" + token.getPrincipal());
ice.printStackTrace();
}catch (AuthenticationException e){
System.out.println("i don't k");
}
}
自定义Reaml加上盐值:
//进行登录的认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//明显的知道:这个authenticationToken就是UsernamePasswordtoken
UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
String username = token.getUsername(); //拿到用户名(注:这个用户名是传过来的)
//这里根据用户名去获取密码(如果没有获取到,相当于这个用户不存在,就返回陪我)
String password = getByName(username);
if(password==null){
return null;
}
//在这里加盐值需一个ByteSource对象,而Shiro提供了一个ByteSource对象给咱们
ByteSource salt = ByteSource.Util.bytes("itsource");
//创建一个简单的身份信息(把用户名与密码放进去-注:它会自动的比较获取的密码与你传过来的密码)
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username,password,salt,getName());
return authenticationInfo;
}
private String getByName(String username) {
if("admin".equals(username)){
// 4a95737b032e98a50c056c41f2fa9ec6: 123456 迭代10次不加盐的结果
// 831d092d59f6e305ebcfa77e05135eac: 123456 迭代10次加盐(itsource)的结果
return "831d092d59f6e305ebcfa77e05135eac"; //修改为加密加盐后的数据
}else if("guest".equals(username)){
return "abcd";
}
return null;
}
Shiro集成Spring:我们的项目基本都是通过Spring来管理bean的,如果要想使用Shiro,那就要把shiro集成到Spring。集成Spring的核心就是把shiro框架的核心类(SecurityManager,Subject,Realm)交给Spring管理
导入集成的包: <dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-allartifactId>
<version>1.4.0version>
<type>pomtype>
dependency>
<dependency>
<groupId>org.apache.shirogroupId>
<artifactId>shiro-springartifactId>
<version>1.4.0version>
dependency>
web.xml配置文件:
<filter>
<filter-name>shiroFilterfilter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxyfilter-class>
<init-param>
<param-name>targetFilterLifecycleparam-name>
<param-value>trueparam-value>
init-param>
filter>
<filter-mapping>
<filter-name>shiroFilterfilter-name>
<url-pattern>/*url-pattern>
filter-mapping>
配置过滤器:
/s/login.jsp = anon
/** = authc