Spring项目整合shiro
先将一下shiro工作的一个流程,代码示例在后面,想要直接看代码的朋友可以直接下滑。
shrio认证流程
1、通过配置文件创建 SecurityManager类。
2、用户登录时 创建 UsernamePasswordToken 类的一个对象(token)。
3、通过SecurityUtils.getSubject().login(token) 方法 提交认证。
4、SecurityManager 由 RealmAuthentication 进行认证。
5、RealmAuthentication 调用自定义的Realm对象(传入token)。
6、Real对象根据传入的token 根据账号查询用户信息。
1> 查询到 -> 将用户信息返回。
2> 否则返回null
7、RealmAuthentication 接收到realm 返回的信息
1> null 抛出 unknownAccountException
2> 找到用户(1、realm返回的密码与token对比 ,若不相同则返回IncorrectCredentialsException)
shiro授权流程
1、构建SecurityManager。
2、Subject.isPermitted()授权。
3、SecurityManager.isPermitted()、执行授权。
4、Authorizer 执行授权。
5、根据身份获取资源权限信息。
1、对Subject授权 调用isPermitted(“permission串”)方法。
2、SecurityManager执行授权–> 通过ModularRealmAuthorizer 执行。
3、ModularRealmAuthorizer 执行 自定义realm从数据库查询权限数据。调用realm的授权方法:doGetAuthorizationInfo()。
4、realm 将权限数据返回给RealmAuthorizer
5、RealmAuthorizer调用PermissionReslover 进行权限串对比。
6、对吧isPermitted中“permission串” 是否在realm查询的权限数据中。
代码示例
1、shiro配置类
@Configuration
public class ShiroConfig {
// 1.配置 shiroFilter
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
DefaultShiroFilterChainDefinition chain = new DefaultShiroFilterChainDefinition();
//哪些请求可以匿名访问
chain.addPathDefinition("/toLogin","anon");
chain.addPathDefinition("/doLogin","anon");
chain.addPathDefinition("/statics/**", "anon");
return chain;
}
// 2. 配置SecurityManager
@Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm());
return securityManager;
}
// 3. 配置自定义的Realm
@Bean
public UserRealm userRealm() {
UserRealm userRealm = new UserRealm();
//给realm认证类,添加加密方式。加密原理:md5
userRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return userRealm;
}
// 3.1 配置salt加密
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("md5");//设置加密方式
hashedCredentialsMatcher.setHashIterations(56);//加密循环的次数
return hashedCredentialsMatcher;
}
// 4.初始化Shiro的生命周期
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**
* setUsePrefix(false)用于解决一个奇怪的bug。在引入spring aop的情况下。
* 在@Controller注解的类的方法中加入@RequiresRole等shiro注解,会导致该方法无法映射请求,
* 导致返回404。加入这项配置能解决这个bug
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager());
return authorizationAttributeSourceAdvisor;
}
@Bean
@DependsOn("lifecycleBeanPostProcessor")
public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
// defaultAdvisorAutoProxyCreator.setUsePrefix(true);
defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
return defaultAdvisorAutoProxyCreator;
}
}
2、创建自定义的Realm类
public class UserRealm extends AuthorizingRealm {
@Resource
private XxUserService xxUserService;
@Resource
private XxRoleService xxRoleService;
@Resource
private UserRoleService userRoleService;
private static Logger logger = LoggerFactory.getLogger(UserRealm.class);
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
logger.info("------------开始进入授权方法-------------");
//获取登录用户名
String userName = (String) principalCollection.getPrimaryPrincipal();
//根据用户名去数据库查询用户信息
XxUser xxUser = xxUserService.findByUserName(userName);
List<UserRole> userRoles = userRoleService.findByUserId(xxUser.getUserId());
//角色列表
List<String> roleNames = new ArrayList<>(userRoles.size());
for (UserRole userRole : userRoles) {
roleNames.add(xxRoleService.findByRoleId(userRole.getRoleId()).getRoleName());
}
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.addRoles(roleNames);
//同时,还和获取用户对应的权限集合
//simpleAuthorizationInfo.addStringPermissions(new ArrayList<>());
logger.info("------------授权成功!!-------------");
return simpleAuthorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
//1.从token中获取用户名
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
String username = token.getUsername();
String password = new String(token.getPassword());
String md5Pwd = new SimpleHash("md5",password, ByteSource.Util.bytes(username),56).toHex();
XxUser xxUser = xxUserService.findByUserName(token.getUsername());
if (xxUser == null){
throw new UnknownAccountException("账号不存在");
}else{
//用户存在则验证密码
if ( !xxUser.getPassword().equals(md5Pwd)){
throw new IncorrectCredentialsException("密码错误");
}
}
return new SimpleAuthenticationInfo(xxUser, xxUser.getPassword(), ByteSource.Util.bytes(xxUser.getUserName()), getName());
}
//得到密码
private static String getMd5Password(String password,String saltStr){
String hashAlgorithmName = "MD5";//加密方式
ByteSource salt = ByteSource.Util.bytes(saltStr);//以账号作为盐值
int hashIterations = 56;// 加密56次(注意这里配置的值要与shiro的配置文件中配置的值保持一致。否则无法解密
SimpleHash hash = new SimpleHash(hashAlgorithmName, password, salt, hashIterations);
System.out.println(hash.toString());
return hash.toString();
}
public static void main(String[] args) {
getMd5Password("123456","admin");
}
}
3、登录时检验
UsernamePasswordToken token = new UsernamePasswordToken((String)paramMap.get("userName"), (String)paramMap.get("password"));
/*
下面这段代码进行异常处理,若产生异常则表示登录失败。
*/
SecurityUtils.getSubject().login(token);//这一步操作成功,则表示用户登录成功
4、接口权限管理
上面提到已经将当前用户的角色和权限获取到。
在接口上使用注解
//对角色控制
@RequiresRoles(value = {"role1";"role2"},logical = Logical.OR)
//对权限控制
@RequiresPermissions(value = {"pre1";"pre2"},logical = Logical.AND)
如果有多个权限/角色验证的时候中间用“,”隔开,默认是所有列出的权限/角色必须同时满足才生效。但是在注解中有logical = Logical.OR这块。这里可以让权限控制更灵活些。
如果将这里设置成OR,表示所列出的条件只要满足其中一个就可以,如果不写或者设置成logical = Logical.AND,表示所有列出的都必须满足才能进入方法。
//需要登录
@RequiresAuthentication
//无需登录
@RequiresGuest
这有一篇博文,作者已经详细讲解了shiro关于权限控制的5个注解
http://blog.csdn.net/w_stronger/article/details/73109248