Spring 简单整合Shiro
本博文主要是简单记录一下Shiro整合Spring的一些基本配置,并不会过多讲述其中的细节。其实我觉得我自己练手写的破网页直接用拦截器或者过滤器或者更简单粗暴在controller中判断权限就已经足够了,但是这一套规范还是学一下把…
Maven dependency
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>${shiro.version}</version>
</dependency>
Spring and Shiro : Hello-World
1.引入上述Maven依赖.
2.配置web.xml:拦截url(很好理解,既然是为了安全,那我肯定要知道你想访问哪一些资源).
<!-- shiro filter -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
3.ApplicationContext.xml配置:Shiro中最最重要的莫过于SecurityManager,Object,Realm(下列运用自定义的Realm)了,利用Spring的依赖注入能很好地配置它们,当然还要配置一个ShiroFilterFactoryBean(下面的一些参数根据你个人需求,只提例子)
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="login.html"/>
<property name="unauthorizedUrl" value="403.html"/>
<property name="filterChainDefinitions">
<value>
/static/**=anon // 静态资源略过
/login.html = anon
/loginHandler = anon
/testPerms = perms["user:delete"]
/testRoles = roles["admin"]
/testUpdate = roles["root"]
/* = authc
</value>
</property>
</bean>
<!-- security Manager -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager" p:realm-ref="realm"/>
<!-- realm -->
<bean id = "realm" class="com.dadagum.realm.UserRealm" p:credentialsMatcher-ref="credentialsMatcher"/>
<!-- HashedCredentialsMatcher : use to encrypt password -->
<bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher" p:hashAlgorithmName="md5" p:hashIterations="1"/>
4.实现自定义Realm(此例子中就是UserRealm):继承AuthorizingRealm,重写方法,注意的是,doGetAuthenticationInfo是验证部分(例如此例子就是一个登陆验证),因为引入了HashedCredentialsMatcher,所以密码是否匹配的工作就交给Shiro来干了,可以看到,只需要提供数据库中相对应密码和盐值(假设用户名存在)。至于doGetAuthorizationInfo,如果调用了这个方法,说明登陆已经通过了,那么就要获取用户的对应角色和授权,本例子中假设登陆的用户具有”admin”角色和”user:delete权限”
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserDao userDao;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//获取角色角色、授权
String userName =(String) principalCollection.getPrimaryPrincipal();
//获取角色和权限
Set<String> roles = userDao.getRolesByUserName(userName);
Set<String> permissions = userDao.getPermissionsByUserName(userName);
//返回授权对象
SimpleAuthorizationInfo s = new SimpleAuthorizationInfo();
s.setStringPermissions(permissions);
s.setRoles(roles);
return s;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 传来的用户名
String userName= authenticationToken.getPrincipal().toString();
//获取数据库中的密码,盐
String password = userDao.getPasswordByUserName(userName);
String salt = userDao.getSaltByUserName(userName);
//认证信息里存放账号密码, getName() 是当前Realm的继承方法,通常返回当前类名
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(userName,password, ByteSource.Util.bytes(salt),getName());
return info;
}
}
5.测试
①编写Controller,例如提供的方法如下(下面部分只是顺便演示检验用户是否具有相对的角色或者权限)
@RequestMapping(value = "/loginHandler", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
@ResponseBody
public String loginHandler(User user, String s){
System.out.println(user);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
try {
subject.login(token);
} catch (AuthenticationException e) {
System.out.println("登陆失败");
return e.getMessage();
}
if(!subject.isAuthenticated()) System.out.println("登陆失败");
if (subject.hasRole("admin")) System.out.println("具有管理员权限");
try {
subject.checkPermission("user:delete");
System.out.println("有删除用户的权限");
} catch (AuthorizationException e) {
System.out.println("没有删除用户的权限");
}
try {
subject.checkPermission("user:update");
System.out.println("有更新用户信息的权限");
} catch (AuthorizationException e) {
System.out.println("没有更新用户信息的权限");
}
return "登陆成功";
}
@RequestMapping(value = "/testPerms", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
@ResponseBody
public String testPerms(){
return "testPerms success";
}
@RequestMapping(value = "/testUpdate", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
@ResponseBody
public String testUpdate(){
return "testUpdate success";
}
@RequestMapping(value = "/testRoles", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
@ResponseBody
public String testRoles(){
return "testRoles0 success";
}
②webapp下创建login.html,提供登陆表单,并且提供User类,注意User类的数据域名称和表单的input标签中的属性名称一致即可.为了演示访问一些没有权限的页面会发生什么事,我们在xml文件中配置中说明了<property name="unauthorizedUrl" value="403.html"/>
,那么就在webapp下建一个403.html吧
③登陆后依次访问testPerms,testUpdate,testRoles,发现除了testUpdate会装跳到403页面,其他都没问题。这是因为配置文档中说明了这些权限。
Hello-World程序就到此结束了。另外,配置文件中可以对一个url进行多个角色或者权限的规定,例如/testAll = roles["admin", "root"]
,那么需要同时具备”admin”和”root”的角色才可以访问,那么如果我想要具有这两个角色其中之一就可以访问呢 ,这个时候roles就不好使了,需要自定义一个过滤器:
①编写继承AuthorizationFilter的类(通过父类获得当前subject,参数Object其实就是例如”admin”,”root”之类的角色定义,这个逻辑也恨简单,如果没有参数,说明没有限制,放行;如果有内容,,判断是否包含列表中的角色)
public class RolesOrFilter extends AuthorizationFilter {
@Override
protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {
Subject subject = getSubject(servletRequest, servletResponse);
String[] roles = (String[]) o;
if (roles == null || roles.length == 0) return true;
for (String role : roles)
if (subject.hasRole(role)) return true;
return false;
}
}
②ApplicationContext.xml中加入上述类:<bean id="rolesOrFilter" class="com.dadagum.filter.RolesOrFilter"/>
③shiroFilter增加为:
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<property name="loginUrl" value="login.html"/>
<property name="unauthorizedUrl" value="403.html"/>
<property name="filterChainDefinitions">
<value>
/static/**=anon // 静态资源略过
/login.html = anon
/loginHandler = anon
/testPerms = perms["user:delete"]
/testRoles = roles["admin"]
/testUpdate = roles["root"]
/testAll = rolesOr["admin","root"]
/* = authc
</value>
</property>
<property name="filters">
<util:map>
<entry key="rolesOr" value-ref="rolesOrFilter"/>
</util:map>
</property>
</bean>
之后发现登录后即使只有admin角色也可以访问/testAll了
基于注解配置访问权限
1.pom.xml加入aspectJ的weaver jar包,然后在ApplicationContext.xml中加入
<!-- use annotation -->
<aop:config proxy-target-class="true"/>
<bean class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor" p:securityManager-ref="securityManager"/>
2.使用注解,表明具有admin角色才可以访问
@RequiresRoles("admin")
@RequestMapping(value = "/testRole", method = RequestMethod.GET, produces = "application/json;charset=UTF-8")
@ResponseBody
public String testRole(){
return "testRole成功";
}
session
待续