结合《spring项目框架的搭建(1):基本搭建》代码,在此基础上完成shiro的整合。
整合前的准备
- 需要引入shiro相关的jar
- 需要一个realm给登录用户进行认证授权
- 添加shiro的配置文件,指定url的过滤规则
- web.xml配置shiro过滤
- 登录控制器调用
- pom.xml内容追加一下几个依赖
<!--shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
<shiro.version>1.4.0</shiro.version> 表示shiro使用的版本号,这里使用的当前时间的最新版本。
- 创建认证授权的Realm,这个类主要的作用是进行登录用户的身份认证和权限认证。
新建一个ShiroDbRealm.java类继承AuthorizingRealm,复写父类中的两个方法
doGetAuthorizationInfo,AuthenticationInfo;使用 @Component注解将对象交给容器管理。
伪代码如下:
/**
* 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用.
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//获取当前登录的用户名,等价于(String)principals.fromRealm(this.getName()).iterator().next()
String currentUsername = (String)super.getAvailablePrincipal(principals);
Admin admin=adminService.findByUserName(currentUsername);
List<String> roles = new ArrayList<String>();//存放该用户具备的角色
List<String> permissions = new ArrayList<String>();//存放该用户具备的权限
//查询用户拥有的角色和对应的权限
List<AdminRole> adminRolesList=adminRolesService.findByAdminId(admin.getId());
if(adminRolesList!=null && adminRolesList.size()>0){
Role role;
for (AdminRole adminRoles:adminRolesList){
role=roleService.get(Role.class,adminRoles.getRoleId());
roles.add(role.getRoleName());
RolePermission permission=roleService.findRolePermission(role.getId());
if (permission!=null){
permissions.addAll(permission.getPermissionList());
}
}
}
//用来保存认证好的信息,
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
if (admin.getIdentity()==1){
info.addRole("admin");
info.addStringPermission("*:*");
}else {
info.addRoles(roles);
info.addStringPermissions(permissions);
}
return info;
}
/**
* 认证回调函数,登录时调用.
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token= (UsernamePasswordToken) authenticationToken;
Admin admin=adminService.findByUserName(token.getUsername());
if (admin!=null){
return new SimpleAuthenticationInfo(admin.getUsername(),admin.getPassword(),admin.getRealName());
}else {
return null;
}
}
AuthenticationInfo登录成后会执行此方法,但是需要在登录成功后的位置显示调用subject.login(token).
身份认证成功后会进入doGetAuthorizationInfo进行权限认证和存储。
- 配置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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd">
<description>Shiro安全配置</description>
<!-- Shiro 安全管理器-->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="shiroDbRealm"/>
</bean>
<!-- Shiro 过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<!--shiro 核心权限管理器 必须配置-->
<property name="securityManager" ref="securityManager"/>
<!--身份认证失败跳转的登录页面-->
<property name="loginUrl" value="/admin/login"/>
<!--认证成功跳转的指定页面-->
<property name="successUrl" value="/admin/index"/>
<!--权限认证失败跳转的指定页面-->
<property name="unauthorizedUrl" value="/admin/unautho"/>
<!--url 过滤规则-->
<property name="filterChainDefinitions">
<value>
/admin/login = anon
/admin/doLogin = anon
/admin/logout = authc
/admin/unautho=anon
<!--/admin/** = [*:*]-->
</value>
</property>
</bean>
<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!-- 开启Shiro注解 AOP式方法级权限检查 -->
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
<property name="proxyTargetClass" value="true"/>
</bean>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
</beans>
同时在spring.xml中添加 <import resource="spring-shiro.xml"/>
- 配置web.xml
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<!-- 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理 -->
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>shiroFilter</filter-name>
<url-pattern>/admin/*</url-pattern>
</filter-mapping>
- LoginController.java
@RequestMapping(value = "doLogin",method = RequestMethod.POST)
public ApiBean doLogin(String username, String password, Boolean rememberMe, HttpSession session, HttpServletRequest request){
ApiBean apiBean;
if (StrKit.notNull(username,password)) {
//验证用户名密码是否正确
JSONObject loginRes=adminService.login(username,password);
if (loginRes.getBoolean("login")){
Admin admin= (Admin) loginRes.get("admin");
UsernamePasswordToken token = new UsernamePasswordToken(admin.getUsername(),admin.getPassword());
Subject subject = SecurityUtils.getSubject();
subject.login(token);
apiBean= ApiBean.responseSuccess("登录成功");
} else {
apiBean= ApiBean.responseError(loginRes.getString("msg"));
}
} else {
apiBean= ApiBean.responseError("用户名密码错误");
}
return apiBean;
}
当subject.login(token)之后,会进入到自定义的realm中的AuthenticationInfo方法,该方法执行完成后会执行doGetAuthorizationInfo方法。在这里我显示的调用了service里的方法去验证用户名密码是否正确,在正确之后再将用户交由shiro进行二次身份核实和权限认证。