shiro是权限管理框架。权限管理是对用户的身份认证和访问权限验证。
shiro涉及的三个对象:
1)Subject:主体
访问系统的用户,主体可以是用户、程序等,进行认证的都称为主体;
2)Principal:身份信息
是主体(subject)进行身份认证的标识,标识必须具有唯一性,如用户名、手机号、邮箱地址等,一个主体可以有多个身份,但是必须有一个主身份(Primary Principal)。
3)credential:凭证信息
只有主体自己知道的安全信息,如密码、证书等。
shiro执行逻辑
shiro与springmvc的集成
1.pom.xml引入
2.配置web.xml
<!-- shiro 过滤器 start -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<!-- 设置true由servlet容器控制filter的生命周期 -->
<init-param>
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- shiro 过滤器 end -->
3.配置spring-shiro.xml
<?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:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.1.xsd">
<description>权限配置</description>
<!-- Realm实现 -->
<bean id="userRealm" class="com.sdhsie.essos.lys.login.component.UserRealm">
<property name="cachingEnabled" value="false"/>
</bean>
<!-- 安全管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="userRealm"/>
<property name="cacheManager" ref="cacheManager"/>
</bean>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
<!-- Shiro生命周期处理器-->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
<property name="cacheManagerConfigFile" value="classpath:ehcache.xml"></property>
</bean>
<!-- 基于Form表单的身份验证过滤器 -->
<bean id="formAuthenticationFilter" class="com.sdhsie.essos.lys.login.component.MyFormAuthenticationFilter">
<property name="usernameParam" value="username"/>
<property name="passwordParam" value="password"/>
<property name="rememberMeParam" value="rememberMe"/>
</bean>
<bean id="rolesAuthorizFilter" class="com.sdhsie.essos.lys.login.component.MyRolesAuthorizationFilter"> </bean>
<!-- Shiro的Web过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!-- shiro的核心安全接口 -->
<property name="securityManager" ref="securityManager"/>
<!-- 要求登录时的链接 -->
<!--<property name="loginUrl" value="/login"/>-->
<!-- 登陆成功后要跳转的连接 -->
<!--<property name="successUrl" value="/" />-->
<property name="filters">
<util:map>
<entry key="authc" value-ref="formAuthenticationFilter"/>
<entry key="roles" value-ref="rolesAuthorizFilter"/>
</util:map>
</property>
<property name="filterChainDefinitions">
<value>
/ = anon
/app/ = anon
/doLogin = anon
/registerDriver = anon
/app/**/*.js = anon
/app/**/*.css = anon
/app/**/*.gif = anon
/app/**/*.png = anon
/app/**/*.jpg = anon
/jsp/**/*.jsp = anon
/jsp/**/*.html = anon
/**/noauth = anon
/logout = anon
/workinformtruckweb/**=roles[admin]
/user/deleteUser/** = perms[USER:DELETE]
/** = authc
</value>
</property>
</bean>
</beans>
1)声明SecurityManager,注入自定义的realm。定义身份验证和权限授权。
2)声明Shiro的WEB过滤器,必须引入securityManager,WEB过滤器主要用于URL的访问控制,控制哪些URL需要权限控制,哪些不需要权限控制
anon:表示不需要权限控制
authc:表示需要登录后才能访问
roles[admin]: 表示有admin角色的才能访问
perms[USER:DELETE]:表示有USER:DELETE权限的才能访问
下面会具体代码说明roles和perms的赋值
4.自定义Realm
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserInfoService userInfoService;
/**
* 表示根据用户身份获取权限信息并授权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
Set<String> roles = Sets.newHashSet();
roles.add("超级管理员");
authorizationInfo.setRoles(roles);
return authorizationInfo;
}
/**
* 表示获取身份验证信息
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token){
if(token.getPrincipal()==null || token.getCredentials()==null){
return null;
}
UsernamePasswordToken upToken = (UsernamePasswordToken)token;
String userName = upToken.getUsername();
String psw = new String(upToken.getPassword());
if(LoginController.PSW_FLAG.equals(psw)){
}else{
UserInfo userInfo = userInfoService.checkByUserName(userName);
if(userInfo == null) {
throw new UnknownAccountException();//没找到帐号
}else{
psw = userInfo.getPassword();
}
}
//交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配,如果觉得人家的不好可以自定义实现
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
userName, //用户名
psw, //密码
null, //salt=username+salt
getName() //realm name
);
return authenticationInfo;
}
@Override
protected void assertCredentialsMatch(AuthenticationToken authcToken,
AuthenticationInfo info) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
super.assertCredentialsMatch(token, info);
}
}
doGetAuthorizationInfo里给roles造了假数据
5.自定义登录验证过滤器
public class MyFormAuthenticationFilter extends FormAuthenticationFilter {
@Override
protected boolean preHandle(ServletRequest request, ServletResponse response) throws Exception {
Subject logUser = SecurityUtils.getSubject();
//电脑版可使用cookie-session保持会话信息
if(logUser.isAuthenticated()){
return super.preHandle(request, response);
}
return super.preHandle(request, response);
}
//处理跨域问题
public static void setResponse(HttpServletResponse res, HttpServletRequest req){
//HttpServletResponse res = (HttpServletResponse) response;
res.setHeader("Access-Control-Allow-Origin",req.getHeader("origin"));
res.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE, PUT");
res.setHeader("Access-Control-Max-Age", "3600");
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
res.setHeader("Access-Control-Allow-Credentials", "true");
}
//未登录访问被拒绝时调用
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
try {
HttpServletRequest req = (HttpServletRequest)request;
HttpServletResponse res = (HttpServletResponse)response;
//。。。。
//返回false自己处理,不再使用shiro的filter
return false;
}
}
AccessControlFilter的onPreHandle调用isAccessAllowed和onAccessDenied
public boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
return isAccessAllowed(request, response, mappedValue) || onAccessDenied(request, response, mappedValue);
}
6.点击登陆请求的控制层
public Object doLogin(String username, String password, String vercode,HttpServletRequest request) throws IOException {
String msg = null;
Integer code = 0;
Object data = null;
try {
// 登录,即身份验证
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
subject.login(token);
// 在线用户维护
//loginService.onlineUserList(getSession(), userId);
}catch (UnknownAccountException e) {
msg = "您输入的用户名不存在!";
code = 1;
}catch (IncorrectCredentialsException e) {
msg = "您输入的密码有误!";
code = 2 ;
}catch (AuthenticationException e) {
msg = "登陆异常,请重新登陆!";
code = 3;
}catch (RuntimeException e){
msg = "账户被注销!";
code = 4;
}
两个过滤情形:
1)账户登录:
点击登录按钮,执行 subject.login(token);后会调用抽象类AuthenticatingRealm.getAuthenticationInfo方法,然后由实现类
UserRealm.doGetAuthenticationInfo方法执行验证密码。
2)未登录直接访问url:
对于请求地址符合authc过滤,过滤器会拦截url验证是否已经登录,执行AccessControlFilter的onPreHandle。先执行isAccessAllowed,若返回true,那么过滤器将直接放行,若返回false,执行或运算符后的onAccessDenied方法,那么再由其子类中的onAccessDenied方法处理后续逻辑。
关于shiro过滤器更详细的源码分析,请看
https://www.cnblogs.com/LeeScofiled/p/10511948.html