代码:https://github.com/jxq0816/springmvc_framework
参考:http://shiro.apache.org/spring.html
一、pom.xml 加入shiro的dependency
<!-- SECURITY begin -->
<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-cas</artifactId>
<version>${shiro.version}</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>${shiro.version}</version>
</dependency>
<!-- SECURITY end -->
二、web.xml 配置shiro过滤器
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:/spring-context.xml
classpath:/spring-context-shiro.xml
</param-value>
</context-param>
<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>
三、resources目录下新建spring-context-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.1.xsd"
default-lazy-init="true">
<description>Shiro Configuration</description>
<!-- Shiro权限过滤过滤器定义 -->
<bean name="shiroFilterChainDefinitions" class="java.lang.String">
<constructor-arg>
<value>
/user/login = anon
/user/logout = anon
/** = authc
</value>
</constructor-arg>
</bean>
<!-- 安全认证过滤器 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
<property name="securityManager" ref="securityManager" />
<property name="loginUrl" value="/user/login" />
<property name="successUrl" value="/user/login" />
<property name="filterChainDefinitions">
<ref bean="shiroFilterChainDefinitions"/>
</property>
</bean>
<!-- 继承自AuthorizingRealm的自定义Realm,即指定Shiro验证用户登录的类为自定义的MyRealm.java -->
<bean id="myRealm" class="com.week7i.share.security.SystemAuthorizingRealm"/>
<!-- 定义Shiro安全管理配置 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
<property name="realm" ref="myRealm" />
</bean>
</beans>
四、spring-mvc-dispatcher.xml 前端控制器中加入如下配置,这样可以支持@RequiresRoles的相关权限过滤
<!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
<!-- 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证 -->
<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>
五、自定义realm
package com.week7i.share.security;
import com.week7i.share.controller.controller;
import com.week7i.share.service.SystemService;
import com.week7i.share.util.SpringContextHolder;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.stereotype.Service;
/**
* Created by jiangxingqi on 2017/7/19.
*/
@Service
public class SystemAuthorizingRealm extends AuthorizingRealm {
private static SystemService systemService;
private static Logger logger = Logger.getLogger(controller.class);
/**
* 获取系统业务对象
*/
public SystemService getSystemService() {
if (systemService == null){
systemService = SpringContextHolder.getBean(SystemService.class);
}
return systemService;
}
/**
* 授权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String currentUsername = (String)super.getAvailablePrincipal(principals);
SimpleAuthorizationInfo simpleAuthorInfo = new SimpleAuthorizationInfo();
if(StringUtils.isNotEmpty(currentUsername)){
//添加一个角色,不是配置意义上的添加,而是证明该用户拥有admin角色
String roleName=systemService.getRoleByUserName(currentUsername);
if(StringUtils.isNotEmpty(roleName)){
simpleAuthorInfo.addRole(roleName);
logger.info("已为用户["+currentUsername+"]赋予了["+roleName+"]角色");
}
//添加权限
//simpleAuthorInfo.addStringPermission("admin:manage");
return simpleAuthorInfo;
}
return simpleAuthorInfo;
}
/**
* 认证
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//获取基于用户名和密码的令牌
//实际上这个authcToken是从LoginController里面currentUser.login(token)传过来的
UsernamePasswordToken authToken = (UsernamePasswordToken)token;
logger.info("MyRealm.doGetAuthenticationInfo.token="+token);
//此处无需比对,比对的逻辑Shiro会做,我们只需返回一个和令牌相关的正确的验证信息
//第一个参数填登录用户名,第二个参数填合法的登录密码
String username=authToken.getUsername();
if(StringUtils.isEmpty(username)==false) {
String password=getSystemService().login(username);
if(StringUtils.isEmpty(password)==false){
AuthenticationInfo authInfo = new SimpleAuthenticationInfo(username, password, this.getName());
return authInfo;
}
}
//没有返回登录用户名对应的SimpleAuthenticationInfo对象时,就会在LoginController中抛出UnknownAccountException异常
return null;
}
}
六、controller
package com.week7i.share.controller;
/**
* Created by jiangxingqi on 2017/2/9.
*/
import com.week7i.share.service.SystemService;
import org.apache.log4j.Logger;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
import java.util.Map;
/**
* Created by jiangxingqi on 2017/2/9.
*/
@Controller
@RequestMapping("user")
public class controller {
private static Logger logger = Logger.getLogger(controller.class);
@Autowired
private SystemService service;
@RequestMapping(value = "index")
public String index(){
return "index";
}
@RequiresRoles("admin")
@RequestMapping(value = "back")
@ResponseBody
public Map fun(){
Map map=new HashMap();
String password=service.login("admin");
map.put("password",password);
return map;
}
@RequestMapping(value = {"login",""},method= RequestMethod.GET)
public String login(){
return "login";
}
@RequestMapping(value = {"login"},method= RequestMethod.POST)
public String loginSubmit(String username,String password,Model model){
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
token.setRememberMe(true);
logger.debug("DefaultController.login#token="+token);
Subject currentUser = SecurityUtils.getSubject();
try {
//在调用了login方法后,SecurityManager会收到AuthenticationToken,并将其发送给已配置的Realm执行必须的认证检查
//每个Realm都能在必要时对提交的AuthenticationTokens作出反应
//所以这一步在调用login(token)方法时,它会走到MyRealm.doGetAuthenticationInfo()方法中,具体验证方式详见此方法
logger.debug("user[" + username + "]do login checking");
currentUser.login(token);
logger.debug("user[" + username + "]authentication success");
}catch(UnknownAccountException uae){
logger.debug("user[" + username + "]UnknownAccountException");
model.addAttribute("error_msg", "UnknownAccountException");
}catch(IncorrectCredentialsException ice){
logger.debug("user[" + username + "]IncorrectCredentialsException");
model.addAttribute("error_msg", "IncorrectCredentialsException");
}catch(LockedAccountException lae){
logger.debug("user[" + username + "]LockedAccountException");
model.addAttribute("error_msg", "LockedAccountException");
}catch(ExcessiveAttemptsException eae){
logger.debug("user[" + username + "]ExcessiveAttemptsException");
model.addAttribute("error_msg", "ExcessiveAttemptsException");
}catch(AuthenticationException ae){
//注意:这个必须放在后面,因为这个异常可以处理所有认证失败的情况
model.addAttribute("error_msg", "authentication faild");
}
//验证是否登录成功
if(currentUser.isAuthenticated()){
logger.debug("user[" + username + "]authentication success");
return "index";
}
token.clear();
return "login";
}
@RequestMapping("logout")
public String logout(){
SecurityUtils.getSubject().logout();
return "login";
}
}
六、login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${error_msg}
<form action="/user/login" method="POST">
姓名:<input type="text" name="username"/><br/>
密码:<input type="password" name="password"/><br/>
<input type="submit" value="确认"/>
</form>
</body>
</html>