首先引入Shiro的依赖包
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
在我们的wed.xml中加入我们的shiro过滤器
<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>
在Spring配置Shiro中的文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd"
>
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"></property>
<!-- 配置当权限不足的时候返回的路径 -->
<property name="loginUrl" value="/login/notLoggedIn.do"></property>
<property name="unauthorizedUrl" value="/login/lackOfAuthority.do"></property>
<property name="filterChainDefinitions">
<!-- 在value中为访问的路径加入过滤器来作为权限的拦截,可以放shiro中默认的过滤器,也可以自己写过滤器放进去 -->
<value>
<!-- anon过滤器:无参,开放权限,可以理解为匿名用户或游客 -->
/login/auth.do=anon
<!-- aesToken是本人设计的一个token登陆认证的机制的过滤器,permissionOr是本人设计的一个权限过滤器,而中括号里面的就是访问需要的权限 -->
/worker/**=aesToken,permissionOr[admin:staff_worker,admin:staff]
/admin/**=aesToken,permissionOr[admin:staff_admin,admin:staff]
/payConfig/**=aesToken
/login/out.do=logout
/**/**=aesToken,user
</value>
</property>
<property name="filters">
<!-- 把自己写的过滤器放进map中 -->
<map>
<entry key="aesToken" value-ref="aesToken"/>
<entry key="permissionOr" value-ref="PermissionOrFilter"/>
<entry key="rolesOr" value-ref="RolesOrFilter"/>
</map>
</property>
</bean>
<!-- 自定义filter过滤器 -->
<bean id="aesToken" class="com.southgis.iznaer.shiro.filter.AesFilter" />
<!-- 自定义filter过滤器 -->
<bean id="RolesOrFilter" class="com.southgis.iznaer.shiro.filter.RolesOrFilter" />
<!-- 自定义filter过滤器 -->
<bean id="PermissionOrFilter" class="com.southgis.iznaer.shiro.filter.PermissionOrFilter" />
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="realm"></property>
<property name="subjectDAO" ref="subjectDAO"></property>
</bean>
<bean id="subjectDAO" class="org.apache.shiro.mgt.DefaultSubjectDAO">
<property name="sessionStorageEvaluator" ref="defaultSessionStorageEvaluator"></property>
</bean>
<bean id="defaultSessionStorageEvaluator" class="org.apache.shiro.mgt.DefaultSessionStorageEvaluator">
<property name="sessionStorageEnabled" value="false"></property>
</bean>
<bean id="realm" class="com.southgis.iznaer.shiro.realm.UserRealm">
</bean>
<!-- 开启AoP -->
<aop:config proxy-target-class="true"/>
<!-- 保证 Shiro内部生命周期 -->
<bean class="org.apache.shiro.spring.LifecycleBeanPostProcessor"></bean>
<!-- 开启Shiro授权生效 -->
<bean id="" class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor"></bean>
</beans>
自定义token类
import org.apache.shiro.authc.AuthenticationToken;
/**
* Shiro中的Authentication,主要是检验token时使用
*/
public class AesToken implements AuthenticationToken{
String token;
public AesToken(String token){
this.token=token;
}
public Object getPrincipal() {
// TODO Auto-generated method stub
return token;
}
public Object getCredentials() {
// TODO Auto-generated method stub
return token;
}
}
创建自定义的shiro过滤器,当过滤器return true时返回到shiro权限认证的自定义realm中,把token放在头部,如果检查到没有token的话则视为未登录状态。有token的情况下会根据token来进行用户的权限认证。
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
import org.json.JSONObject;
import com.southgis.iznaer.base.ResponseConstant;
import com.southgis.iznaer.base.ResponseResult;
import com.southgis.iznaer.util.AesToken;
/**
* 通过改过滤器跳到Realm中认证用户信息和权限信息
* @author gyc
* @date 2018-10-18
*/
public class AesFilter extends BasicHttpAuthenticationFilter {
/**
* 过滤方法
*/
protected boolean isAccessAllowed(ServletRequest request,
ServletResponse response, Object object) throws AuthenticationException {
String token=((HttpServletRequest) request).getHeader("token");
ResponseResult jsonResult = new ResponseResult();
//判断请求的请求头是否带上 "Token"
if (token!=null) {
try {
AesToken m=new AesToken(token);
getSubject(request, response).login(m);
return true;
} catch (AuthenticationException e) {
//token 错误
e.printStackTrace();
jsonResult.setCode(ResponseConstant.TOKEN_FAIL_CODE);//返回失败的code
jsonResult.setState(ResponseConstant.FAIL_STATE);//返回失败的state
jsonResult.setDescription("token错误,或token已过期");
jsonResult.setResults(null);
JSONObject jsonObject=new JSONObject(jsonResult);
responseOutWithJson(response,jsonObject.toString());
return false;
}
}
//如果请求头不存在 Token,则可能是执行登陆操作或者是游客状态访问,无需检查 token,直接返回 true
return true;
}
/**
* 以JSON格式输出
* @param response
*/
protected void responseOutWithJson(ServletResponse response,
Object responseObject) {
//将实体对象转换为JSON Object转换
// JSONObject responseJSONObject = JSONObject.fromObject(responseObject);
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.setCharacterEncoding("UTF-8");
httpServletResponse.setContentType("application/json; charset=utf-8");
PrintWriter out = null;
try {
out = httpServletResponse.getWriter();
out.append((String)responseObject);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (out != null) {
out.close();
}
}
}
}
以下两个也是自定的权限过滤器,功能类似shiro中的默认权限。
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
/**
* 自定义权限过滤器,拥有多权限之一的用户可以通过
* @author gyc
* @date 2018-10-18
*/
public class PermissionOrFilter extends AuthorizationFilter {
@Override
protected boolean isAccessAllowed(ServletRequest req,
ServletResponse resp, Object object) throws Exception {
Subject subject = getSubject(req, resp);
String[] permissions = (String[]) object;
if (permissions == null || permissions.length == 0) {
return true;
}
for (String permission : permissions) {
if (subject.isPermitted(permission)) {
return true;
}
}
return false;
}
}
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authz.AuthorizationFilter;
/**
* 自定义角色过滤器,拥有多角色之一的用户可以通过
* @author gyc
* @date 2018-10-18
*/
public class RolesOrFilter extends AuthorizationFilter {
@Override
protected boolean isAccessAllowed(ServletRequest req,
ServletResponse resp, Object object) throws Exception {
Subject subject = getSubject(req, resp);
String[] roles = (String[]) object;
if (roles == null || roles.length == 0) {
return true;
}
for (String role : roles) {
if (subject.hasRole(role)) {
return true;
}
}
return false;
}
}
自定义Reaml,在上文中我们所设置的自定义的shiro过滤器AesFilter 通过后会跳到Reaml中根据我们的token来进行权限的认证和分配。
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.Resource;
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;
public class UserRealm extends AuthorizingRealm {
User user=null;
@Resource(name = "userServiceImpl")
UserService userService;
@Resource(name = "permissionServiceImpl")
PermissionService permissionService;
/**
* 必须重写此方法,不然会报错
*/
@Override
public boolean supports(AuthenticationToken token) {
return token instanceof AesToken;
}
/**
* 默认使用此方法进行用户名正确与否验证,错误抛出异常即可。
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException{
System.out.println("————身份认证方法————");
String token = (String) authenticationToken.getCredentials();
user=userService.findUserByToken(token);
if(this.user== null){
throw new AuthenticationException("token认证失败!");
}
return new SimpleAuthenticationInfo(token, token, getName());
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("————权限认证————");
permissionService.findPermissionByUser(user);
Set<String> permissionSet = new HashSet<String>();
for (Permission permission : permissions) {
permissionSet.add(permission.getName());
}
info.setStringPermissions(permissionSet);
//把permissionSet放入info,info成功return后就可以成功配置和认证权限
return info;
}
}
到这里我们的shiro配置算是配置完毕了。
配置完以后我们可以到刚刚配置关于shiro那一段的spring xml文件配置我们访问的路径所需要的权限。如下:
<property name="filterChainDefinitions">
<!-- 在value中为访问的路径加入过滤器来作为权限的拦截,可以放shiro中默认的过滤器,也可以自己写过滤器放进去 -->
<value>
<!-- anon过滤器:无参,开放权限,可以理解为匿名用户或游客 -->
/login/auth.do=anon
<!-- aesToken是本人设计的一个token登陆认证的机制的过滤器,permissionOr是本人设计的一个权限过滤器,而中括号里面的就是访问需要的权限 -->
/worker/**=aesToken,permissionOr[admin:staff_worker,admin:staff]
/admin/**=aesToken,permissionOr[admin:staff_admin,admin:staff]
/payConfig/**=aesToken
/login/out.do=logout
/**/**=aesToken,user
</value>
</property>
除了可以加入我们自定义的过滤器以外,我们还可以添加shiro默认的过滤器。
Filter | 解释 |
---|---|
anon | 无参,开放权限,可以理解为匿名用户或游客 |
authc | 无参,需要认证 |
logout | 无参,注销,执行后会直接跳转到shiroFilterFactoryBean.setLoginUrl(); 设置的 url |
authcBasic | 无参,表示 httpBasic 认证 |
user | 无参,表示必须存在用户,当登入操作时不做检查 |
ssl | 无参,表示安全的URL请求,协议为 https |
perms[user] | 参数可写多个,表示需要某个或某些权限才能通过,多个参数时写 perms[“user, admin”],当有多个参数时必须每个参数都通过才算通过 |
roles[user] | 参数可写多个,表示是某个或某些角色才能通过,多个参数时写 roles[“admin,user”],当有多个参数时必须每个参数都通过才算通过 |
rest[user] | 根据请求的方法,相当于 perms[user:method],其中 method 为 post,get,delete 等 |
port[8081] | 当请求的URL端口不是8081时,跳转到schemal://serverName:8081?queryString 其中 schmal 是协议 http 或 https 等等,serverName 是你访问的 Host,8081 是 Port 端口,queryString 是你访问的 URL 里的 ? 后面的参数 |
配置完后当用户访问的时候,访问的流程如下:
- AesFilter:判断用户是否带有token,若有进入Reaml
- UserRealm :通过token认证获取到User用户
- UserRealm :通过User用户拿到权限,进行权限认证。
- 通过如perms[user] 、permissionOr[admin:staff_worker]等过滤器。
- 成功访问地址