1.导入jar包(Maven工程):
<!-- apache shiro dependencies -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-all</artifactId>
<version>1.2.3</version>
</dependency>
2.Shiro核心控制器的配置:
<!-- Shiro核心控制器,一定放在struts2过滤器之前 filter-name这个名字的值将来还会在spring中用到 -->
<filter>
<filter-name>shiroFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<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>
3.产生代理类的方式:
下面这行代码放置在applicationContext.xml的事务管理器声明之前。
<!-- 告诉spring生成shiro代理子类时,采用cglib方式生成 -->
<aop:aspectj-autoproxy proxy-target-class="true" />
4.Shiro的配置文件:
<description>Shiro与Spring整合</description>
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<!-- Single realm app. If you have multiple realms, use the 'realms' property instead. -->
<property name="realm" ref="authRealm"/><!-- 引用自定义的realm -->
</bean>
<!-- 自定义Realm域的编写 -->
<bean id="authRealm" class="cn.itcast.shiro.AuthRealm">
<!-- 注入自定义的密码比较器 -->
<property name="credentialsMatcher" ref="customerCredentialsMatcher" ></property>
</bean>
<!-- 自定义的密码比较器 -->
<bean id="customerCredentialsMatcher" class="cn.itcast.shiro.CustomerCredentialsMatcher"></bean>
<!-- filter-name这个名字的值来自于web.xml中filter的名字 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager"/>
<!--登录页面 -->
<property name="loginUrl" value="/index.jsp"></property>
<!-- 登录成功后 -->
<!-- <property name="successUrl" value="/home.action"></property> -->
<property name="filterChainDefinitions">
<!-- /**代表下面的多级目录也过滤 -->
<value>
/index.jsp* = anon
/home* = anon
/sysadmin/login/login.jsp* = anon
/sysadmin/login/loginAction_logout* = anon
/login* = anon
/logout* = anon
/components/** = anon
/css/** = anon
/img/** = anon
/js/** = anon
/plugins/** = anon
/images/** = anon
/js/** = anon
/make/** = anon
/skin/** = anon
/stat/** = anon
/ufiles/** = anon
/validator/** = anon
/resource/** = anon
/** = authc
/*.* = authc </value>
</property>
</bean>
<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!-- 生成代理,通过代理进行控制 -->
<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>
5.在applicationContext.xml中加载Shiro配置文件:
<!--组装其它 配置文件-->
<import resource="classpath:spring/applicationContext-shiro.xml"/>
自定义AuthRealm类:
/**
*Realm域的类需要继承AuthorizingRealm
*/
public class AuthRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
//授权
/**
* 这个方法在什么时候调用?当jsp页面上第一次出现shiro标签时就会自动调用
*/
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection pc) {
//1.从Shiro中取出用户对象
User user = (User)pc.fromRealm(this.getName()).iterator().next();
List<String> permission = new ArrayList<String>();
//2.通过对象关联查询,加载用户的角色列表
Set<Role> roles = user.getRoles();
//3.遍历角色列表,得到每个角色
for (Role role : roles) {
Set<Module> modules = role.getModules();
//对象导航查询,模块列表
for (Module module : modules) {
if(!permission.contains(module)){
permission.add(module.getName());
}
}
}
//生成一个返回值的对象
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermissions(permission);
return info;
}
//认证 token参数代表用户在界面上输入的用户名和密码
/**
* 如果返回值为null,shiro就会抛出异常
*
* 如果返回值不为null,程序就会自动调用密码比较器
*
*/
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { //1.向下转型
final UsernamePasswordToken upToken = (UsernamePasswordToken) token;
//2.调用业务方法,实现根据用户名进行查询
Specification<User> spec = new Specification<User>() {
public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
return cb.equal(root.get("userName").as(String.class), upToken.getUsername());
}
};
List<User> userList = userService.find(spec);
//3.判断
if(userList!=null && userList.size()>0){
User user = userList.get(0);
//三个参数,第一个参数Principal代表用户对象,第二个参数credentials 代表密码 第三个参数realmName代表realm域的名字
return new SimpleAuthenticationInfo(user,user.getPassword(),this.getName());
}
return null;
}
}
自定义CustomerCredentialsMatcher密码比较器类:
/**
* 密码比较器
*/
public class CustomerCredentialsMatcher extends SimpleCredentialsMatcher {
/**
* 执行密码比较
* 第一个参数:用户在界面上输入的用户名和密码
* 第二个参数info:代表用户的全部信息
*
* 返回值:
* true代表密码比较成功
* false代表密码比较失败,Shiro就会抛出异常
*/
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
//1.向下转型
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
//2.调用Md5Hash算法 第一个参数:明文 第二个参数:盐 第三个参数:加盐的次数,代表轮换的次数
//String uipwdEncrypt = new Md5Hash(new String(upToken.getPassword()), upToken.getUsername(), 2).toString();
String uipwdEncrypt = Encrypt.md5(new String(upToken.getPassword()), upToken.getUsername());
//3.可以得到这个用户在数据库中的密文
String dbpwd = info.getCredentials().toString();
return equals(uipwdEncrypt, dbpwd);
}
}
LoginAction登陆Action类:
/**
* @Description: 登录和退出类
*
* 继承BaseAction的作用
* 1.可以与struts2的API解藕合
* 2.还可以在BaseAction中提供公有的通用方法
*/
@Namespace("/")
@Results({
@Result(name="login",location="/WEB-INF/pages/sysadmin/login/login.jsp"),
@Result(name="success",location="/WEB-INF/pages/home/fmain.jsp"),
@Result(name="logout",location="/index.jsp")})
public class LoginAction extends BaseAction {
private static final long serialVersionUID = 1L;
private String username;
private String password;
@Action("loginAction_login")
public String login() throws Exception {
//SSH传统登录方式
//if(true){
// String msg = "登录错误,请重新填写用户名密码!";
// this.addActionError(msg);
// throw new Exception(msg);
//}
//User user = new User(username, password);
//User login = userService.login(user);
//if (login != null) {
// ActionContext.getContext().getValueStack().push(user);
// session.put(SysConstant.CURRENT_USER_INFO, login); //记录session
// return SUCCESS;
//}
//return "login";
if(UtilFuns.isEmpty(username)){
return "login";
}
try {
//1.得到Subject
Subject subject = SecurityUtils.getSubject();
//2.调用登录方法
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
subject.login(token);//调用登录
//3.从Shiro中取出用户信息
User user = (User)subject.getPrincipal();
//4.将用户信息放入session域中
session.put(SysConstant.CURRENT_USER_INFO, user);
} catch (Exception e) {
e.printStackTrace();
request.put("errorInfo", "对不起,用户名或密码错误,登录失败!");
return "login";
}
return SUCCESS;
}
//退出
@Action("loginAction_logout") public String logout(){
session.remove(SysConstant.CURRENT_USER_INFO); //删除session
SecurityUtils.getSubject().logout(); //登出
return "logout";
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}