Shiro教程之四 散列算法和凭证配置
Shiro认证密码一般都需要加密(大部分是md5或hash散列加密)
如果改为密文验证,需要 1.改动一下自定义的Realm类和 2.测试时new出来的 Realm类加入认证的凭证
先来下加密的例子
//五,测试加密 (要引入Shiro包,对应的有此类) @Test public void testMd5Hash(){ String source = "123456";//原文 String salt = "abc";//混淆(可选参数) int num = 2;//散列加密次数(可选参数) Md5Hash hash1 = new Md5Hash(source,salt,num); System.out.println("123456混淆abc散列2次的结果:"+hash1); }
1.改动一下自定义的Realm类
package com.cc8w.shiro; import com.cc8w.entity.UserActivePojo; import com.cc8w.entity.UserPojo; import com.cc8w.service.PermssionService; import com.cc8w.service.RoleService; import com.cc8w.service.UserService; 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.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import java.util.List; /** * Shiro默认使用自带的IniRealm,IniRealm从ini配置文件中读取用户的信息,大部分情况下需要从系统的数据库中读取用户信息,所以需要自定义realm。 * 最基础的是Realm接口,CachingRealm负责缓存处理,AuthenticationRealm负责认证,AuthorizingRealm负责授权,通常自定义的realm继承AuthorizingRealm */ @Component public class ShiroRealm extends AuthorizingRealm { @Autowired private UserService userService; @Autowired private RoleService roleService; @Autowired private PermssionService permssionService; @Override public String getName() { return this.getClass().getSimpleName(); } /* * 登录信息和用户验证信息验证(non-Javadoc) * @see org.apache.shiro.realm.AuthenticatingRealm#doGetAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken) */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //1.从authenticationToken中获取身份信息,其实就是用户的登录名 String username = authenticationToken.getPrincipal().toString(); String password = authenticationToken.getCredentials().toString(); //2.根据用户名查询用户是否存在 UserPojo user=userService.queryUserByUserName(username); System.out.println(user); //返回null说明用户不存在 if(null!=user) { //2.1根据用户名去查询用户拥有哪些角色 List<String> roles= roleService.queryRolesByUserName(user.getUserName()); System.out.println(roles); //2.2根据用户名查询用户拥有哪些权限 List<String> permissions=permssionService.queryPermissionsByUserName(user.getUserName()); UserActivePojo activeUser=new UserActivePojo(user, roles, permissions); //3.返回认证信息(第一个不支持散列加密,第二个可以散列加密) /** * 参数1 用户身份 * 参数2 用户在数据库里面存放的密码 * 参数3 当前类名 */ //SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(activeUser, user.getPassword(), this.getName()); /** * 参数1 传到doGetAuthorizationInfo里面的getPrimaryPrincipal()对象或subject.getPrincipal()-我们封装的实体activeUser * 参数2 hashedCredentials加密后的密码(数据库查询的密码)-user.getPassword() * 参数3 混淆(盐) * 参数4 当前类名 */ SimpleAuthenticationInfo info=new SimpleAuthenticationInfo(principal, hashedCredentials, credentialsSalt, realmName) return info; } return null; } /* * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用,负责在应用程序中决定用户的访问控制的方法(non-Javadoc) * @see org.apache.shiro.realm.AuthorizingRealm#doGetAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection) */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //1.获得用户身份信息(PrincipalCollection有认证回调传来的第一个参数[activeUser]) UserActivePojo activeUser = (UserActivePojo) principalCollection.getPrimaryPrincipal(); System.out.println("doGetAuthorizationInfo"); //2.根据身份信息获取权限数据 SimpleAuthorizationInfo info=new SimpleAuthorizationInfo(); //3.根据用户查询用户的角色 (其实认证方法一并查询出来了,保存在UserActivePojo) List<String> roles = activeUser.getRoles(); if(null!=roles&&roles.size()>0) { info.addRoles(roles);//添加角色 } //4.根据用户查询用户的权限 List<String> permissions=activeUser.getPermissions(); if(null!=permissions&&permissions.size()>0) { info.addStringPermissions(permissions);//添加权限 } /** * 总结:本来授权->查角色和权限都在本方法写(但是前端每查询一次权限,就会回调本方法一次, * 所以直接查数据库,对数据库有压力),所以最后, * 1.把查角色和权限方法写在了认证,然后封装成activeUser传递过来,这样Controller每次查权限,就不用查数据库了,直接在activeUser获取即可. * 2.缓存应该也可以解决 */ return info; } }
2.测试时new出来的 Realm类加入认证的凭证
<和上几篇文章一样,最重要的改动是 new Realm(),之后加入了散列密码相关的设置>
package com.cc8w.test; import com.cc8w.shiro.ShiroRealm; import org.apache.log4j.Logger; import org.apache.shiro.SecurityUtils; import org.apache.shiro.authc.AuthenticationException; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authc.UsernamePasswordToken; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.authz.AuthorizationException; import org.apache.shiro.authz.UnauthenticatedException; import org.apache.shiro.authz.UnauthorizedException; import org.apache.shiro.config.IniSecurityManagerFactory; import org.apache.shiro.crypto.hash.Md5Hash; import org.apache.shiro.mgt.DefaultSecurityManager; import org.apache.shiro.subject.Subject; import org.apache.shiro.util.Factory; import org.apache.shiro.mgt.SecurityManager; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import java.util.Arrays; /** * */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = {"classpath:applicationContext.xml"}) public class TestShiro { private static Logger logger = Logger.getLogger(TestShiro.class); @Autowired private ShiroRealm shiroRealm; public static void main(String[] args) { TestShiro ts = new TestShiro(); ts.testAuth(); } //三,登录测试(自定义Realm) @Test public void testRealmLogin(){ //1.创建一个安全管理器的工厂 Factory<SecurityManager> factory = new IniSecurityManagerFactory(); //2.在工厂中获取安全管理器 DefaultSecurityManager securityManager = (DefaultSecurityManager) factory.getInstance(); //---添加认证凭证配置start --如果Spring环境下(这些都是在xml配置的) //2.1 创建自定义Realm注入到安全管理器 ShiroRealm shiroRealm = new ShiroRealm();//(SpringM在bean控制,并可以配置散列加密相关) //2.1.1设置密码学相关加密方式 HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher(); //2.1.2设置加密方式 credentialsMatcher.setHashAlgorithmName("md5"); //2.1.3设置散列次数 credentialsMatcher.setHashIterations(1); //2.1.4将密码学凭证注入到自定义的Realm类中 shiroRealm.setCredentialsMatcher(credentialsMatcher); //---添加认证凭证配置end --如果Spring环境下(这些都是在xml配置的) securityManager.setRealm(shiroRealm); //3.将securityManager绑定到运行环境 SecurityUtils.setSecurityManager(securityManager); //4.获取Subject对象(将要登录的用户) Subject subject = SecurityUtils.getSubject(); //5.获取要登录用户的token,客户端传递过来的用户名和密码 String username = "zhangsan",password="123456"; UsernamePasswordToken token = new UsernamePasswordToken(username,password); try{ //6.登陆(认证) subject.login(token); logger.info("登录了"); }catch (IncorrectCredentialsException e ){ logger.info("密码不正确"); logger.info(e); }catch (UnknownAccountException e) { System.out.println("没有这个帐号"); }catch (AuthenticationException e) { e.printStackTrace(); } //如果登录成功了,可以获取subject中各种状态了 Boolean isAuth = subject.isAuthenticated(); System.out.println("认证状态:" + isAuth); // 7.授权 分为:基于角色授权 基于资源的授权 //7.1 基于角色授权 boolean permited = subject.hasRole("role1"); System.out.println("这是授权单个:"+permited); boolean hasAllRoles = subject.hasAllRoles(Arrays.asList("role1","role2","role3")); System.out.println("这个授权多个"+hasAllRoles); // 使用check方法进行授权,如果授权不通过会抛出异常 // subject.checkRole("role13"); try { subject.checkRole("roles1"); }catch (UnauthenticatedException e){ logger.info("没有这个角色"); //e.printStackTrace(); }catch (UnauthorizedException e){ logger.info("没有这个权限"); //e.printStackTrace(); } //7.2 基于资源的授权 //isPermitted传入权限标识符 boolean isPermitted = subject.isPermitted("user:query"); System.out.println("单个权限判断:"+isPermitted); boolean isPermittedAll = subject.isPermittedAll("user:query","user:adb","user:add"); System.out.println("多个权限判断"+isPermittedAll); // 使用check方法进行授权,如果授权不通过会抛出异常 try { subject.checkPermission("user:adb"); }catch (UnauthenticatedException e){ logger.info("没有这个角色"); //e.printStackTrace(); }catch (UnauthorizedException e){ logger.info("没有这个权限"); //e.printStackTrace(); } } //四,授权验证(自定义Realm) public void testRealmAuth(){ //其实授权也需要登陆(上面方法第7条之后:就是授权的验证) } }