您的位置:首页 > 编程语言 > Java开发

spring+shiro 配置使用(完整代码篇)

2016-09-07 12:37 369 查看
web安全框架,shiro相比spring security,更轻量级,配置简单易懂,小巧灵活,功能强大,和spring完美结合,shiro上手超级简单,一看就懂,但如果需求较为复杂,仍需要细细研究其中原理,灵活配置。本人才疏学浅,本文仅涉及登录验证,动态权限验证,后面有机会再慢慢研究。

引入Shiro的Maven依赖

<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.3.0</version>
</dependency>

<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>1.3.0</version>
</dependency>

<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.0</version>
</dependency>

<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-quartz</artifactId>
<version>1.3.0</version>
</dependency>


web.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 配置

<!-- 配置权限管理器 -->
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">

<!-- ref对应我们写的realm JuJinShiro -->
<property name="realm" ref="JuJinShiro"/>

<!-- 使用下面配置的缓存管理器 -->
<property name="cacheManager" ref="cacheManager" />

</bean>

<bean id="JuJinShiro" class="com.hd.security.JuJinShiro"></bean>

<bean id="chainDefinitionSectionMetaSource" class="com.hd.security.ChainDefinitionSectionMetaSource">

<property name="filterChainDefinitions">
<value>
<!-- anon表示此地址不需要任何权限即可访问 -->
/css/**=anon
/imgs/**=anon
/js/**=anon
/auth/**=authc
</value>
</property>
</bean>

<!-- 配置shiro的过滤器工厂类,id- shiroFilter要和我们在web.xml中配置的过滤器一致 -->
<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">

<!-- 调用我们配置的权限管理器 -->
<property name="securityManager" ref="securityManager" />

<!-- 配置我们的登录请求地址 -->
<property name="loginUrl" value="/auth/silencelogin" />

<!-- 配置我们在登录页登录成功后的跳转地址 -->
<property name="successUrl" value="/auth/auth" />

<!-- 如果您请求的资源不再您的权限范围,则跳转到无权限地址 -->
<property name="unauthorizedUrl" value="/auth/unauthorized" />

<!-- 权限配置,动态的可以读取数据库 -->
<property name="filterChainDefinitionMap" ref="chainDefinitionSectionMetaSource" />
</bean>

<!-- 会话管理器 -->
<bean id="sessionManager"
class="org.apach
14e53
e.shiro.web.session.mgt.DefaultWebSessionManager">
<property name="globalSessionTimeout" value="1800" />
<property name="deleteInvalidSessions" value="true" />
<property name="sessionValidationSchedulerEnabled" value="true" />
</bean>

<bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager" />
<bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor" />


自定义realm配置

package com.hd.security;

import java.util.List;

import org.apache.shiro.SecurityUtils;
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.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.hd.common.Constants;
import com.hd.dao.mappers.security.PermissionDao;
import com.hd.entity.login.LoginBean;
import com.hd.service.login.LoginService;

@Service
@Transactional
public class JuJinShiro extends AuthorizingRealm{

protected static Logger logger = LoggerFactory.getLogger(JuJinShiro.class);

@Autowired
private LoginService loginService;

@Autowired
private PermissionDao permissionDao;

/**
* 权限认证(仅使用了permission)
*/
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

//权限信息对象info,用来存放查出的用户的所有的角色(role)及权限(permission)
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();

String userId = (String) principalCollection.fromRealm(getName()).iterator().next();

// 查询用户所对应的资源ID
List<String> resources = permissionDao.getResourceForUser(userId);

info.addStringPermissions(resources);

return info;
}

/**
* 登录认证
*/
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken authenticationToken) throws AuthenticationException {

//UsernamePasswordToken对象用来存放提交的登录信息
UsernamePasswordToken token=(UsernamePasswordToken) authenticationToken;

char[] password = token.getPassword();

//查询用户
LoginBean user = new LoginBean();
try {
// 验证登录信息
user = loginService.loginCheck(token.getUsername(),String.valueOf(password));
} catch (Exception e) {
throw new RuntimeException(e.getMessage());
}

//注入session
Session shiroSession = SecurityUtils.getSubject().getSession();
shiroSession.setAttribute(Constants.USER_KEY, user);
shiroSession.setAttribute(Constants.USERNAME_KEY, user.getUser_id());

//将此用户存放到登录认证info中
return new SimpleAuthenticationInfo(user.getUser_id(), String.valueOf(password), getName());
}
}


package com.hd.security;

import org.apache.shiro.config.Ini;
import org.apache.shiro.config.Ini.Section;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;

import com.hd.service.security.PermissionService;

public class ChainDefinitionSectionMetaSource implements FactoryBean<Ini.Section>{

@Autowired
private PermissionService permissionService;

public static String filterChainDefinitions;

/**
* 默认permission字符串
*/
public static final String PREMISSION_STRING ="perms[\"{0}\"]";

@Override
public Section getObject() throws Exception {
// 初始化权限
return permissionService.initPermission(filterChainDefinitions);
}

/**
* 通过filterChainDefinitions对默认的url过滤定义
*
* @param filterChainDefinitions 默认的url过滤定义
*/
public void setFilterChainDefinitions(String filterChainDefinitions) {
this.filterChainDefinitions = filterChainDefinitions;
}

@Override
public Class<?> getObjectType() {
// TODO Auto-generated method stub
return this.getClass();
}

@Override
public boolean isSingleton() {
// TODO Auto-generated method stub
return false;
}

}


权限操作

package com.hd.service.security;

import java.text.MessageFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.shiro.config.Ini;
import org.apache.shiro.config.Ini.Section;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.hd.dao.mappers.security.PermissionDao;
import com.hd.service.BaseService;
import com.hd.utils.ShiroUtils;

@Service
public class PermissionService extends BaseService{

@Autowired
private ShiroFilterFactoryBean shiroFilterFactoryBean;

@Autowired
private PermissionDao permissionDao;

/**
* 默认permission字符串
*/
public static final String PREMISSION_STRING="perms[\"{0}\"]";

protected static final Logger logger = LoggerFactory.getLogger(PermissionService.class);

/**
* 初始化框架权限资源配置
*/
public Section initPermission(String filterChainDefinitions) {
//shiroFilterFactoryBean.setFilterChainDefinitionMap(obtainPermission(filterChainDefinitions));

Section section = obtainPermission(filterChainDefinitions);

logger.info("初始化shiro权限成功...");
return section;
}

/**
* 更新权限资源配置 (强制线程同步)
*/
public void updatePermission(String filterChainDefinitions) {

synchronized (shiroFilterFactoryBean) {

AbstractShiroFilter shiroFilter = null;

try {
shiroFilter = (AbstractShiroFilter) shiroFilterFactoryBean.getObject();
} catch (Exception e) {
logger.error(e.getMessage());
}

// 获取过滤管理器
PathMatchingFilterChainResolver filterChainResolver = (PathMatchingFilterChainResolver) shiroFilter
.getFilterChainResolver();
DefaultFilterChainManager manager = (DefaultFilterChainManager) filterChainResolver.getFilterChainManager();

// 清空初始权限配置
manager.getFilterChains().clear();
shiroFilterFactoryBean.getFilterChainDefinitionMap().clear();

// 重新构建生成
shiroFilterFactoryBean.setFilterChainDefinitions(filterChainDefinitions);
Map<String, String> chains = obtainPermission(filterChainDefinitions);

for (Map.Entry<String, String> entry : chains.entrySet()) {
String url = entry.getKey();
String chainDefinition = entry.getValue().trim().replace(" ", "");
manager.createChain(url, chainDefinition);
}

//清除用户授权信息
ShiroUtils.clearAllCachedAuthorizationInfo();

logger.info("更新shiro权限成功...");
}
}

/**
* 读取配置资源和第三方资源
*
*/
private Section obtainPermission(String filterChainDefinitions) {
Ini ini = new Ini();
ini.load(filterChainDefinitions); // 加载资源文件节点串
Section section = ini.getSection(Ini.DEFAULT_SECTION_NAME); // 使用默认节点

Map<String, String> permissionMap = initOtherPermission();
if (permissionMap != null && !permissionMap.isEmpty()) {
section.putAll(permissionMap);
}

return section;
}

/**
* 读取第三方资源
* @return
*/
public Map<String, String> initOtherPermission() {

Map<String,String> permissionMap = new HashMap<>();

// 获取所有需要权限的接口资源
List<Map<String, Object>> resources = permissionDao.getAllResources();

for (Map<String,Object> resource : resources) {
String code = resource.get("code").toString();
String url = resource.get("url").toString();

permissionMap.put("/**/"+url+"/**", MessageFormat.format(PREMISSION_STRING,code));
}
//permissionMap.put("/**/auth/**", "authc");

return permissionMap;
}
}


登录controller

package com.hd.controller.login;

import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.util.SavedRequest;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.hd.controller.BaseController;
import com.hd.controller.MakeVerifyCodeService;
import com.hd.entity.login.LoginBean;
import com.hd.redis.RedisUtil;
import com.hd.security.ChainDefinitionSectionMetaSource;
import com.hd.service.login.LoginService;
import com.hd.service.security.PermissionService;
import com.hd.utils.CookieTool;
import com.jujin.common.ExceptionHelper;
import com.jujin.common.OpResult;

@Controller
@RequestMapping(value = "")
public class loginControler extends BaseController{

protected static final Logger logger = LoggerFactory.getLogger(loginControler.class);
String verifyCode = "verifyCode";
@Autowired
private PermissionService permissionService;
@Autowired
private LoginService loginService;

/**
* 本系统用户登陆
* @param userid
* @param password
* @return
*/
@RequestMapping(value = "/login",method = RequestMethod.POST)
@ResponseBody
public OpResult login(HttpEntity<LoginBean> model,HttpServletRequest request, HttpServletResponse response) {
OpResult result = new OpResult();
LoginBean login = model.getBody();
String userId = login.getUser_id();
String password = login.getPassword();
if(StringUtils.isEmpty(userId) || StringUtils.isEmpty(password) ){
result.setMsg("输入参数不完整");
result.setStatus(false);
return result;
}

try {

Subject subject = SecurityUtils.getSubject();
Session session = subject.getSession();
String  code =  login.getCode();
if ("1".equals(session.getAttribute("codeIsShow"))) {
String sessionCode = (String) session.getAttribute(verifyCode);
if(code == null){
result.setMsg("请输入验证码");
result.setStatus(false);
return result;
}
if (!StringUtils.isEmpty(code) && !code.equals(sessionCode)) {
result.setMsg("验证码错误,请重新输入");
result.setStatus(false);
return result;
}
}
UsernamePasswordToken token = new UsernamePasswordToken(userId, password);
//登陆认证
subject.login(token);
//登陆日志
logger.info("用户" + userId + "在" + sysdate() + "登陆");
result.setStatus(true);
result.setLoginstatus(1);

// 更新用户权限
permissionService.updatePermission(ChainDefinitionSectionMetaSource.filterChainDefinitions);
logger.info("更新用户[" + this.getLoginUserId(request) + "]权限成功");
} catch (Exception e) {
result.setStatus(false);
result.setLoginstatus(0);
result.setMsg(e.getCause().getMessage());
}
if (supportJsonp(request)) {

jsonpWrapper(request, response, result);

return null;

} else {

return result;
}

}
// 普通图片验证码
@RequestMapping(value = "/verify", method = RequestMethod.GET)
public @ResponseBody
Object getVerifyImage(HttpServletRequest request,
HttpServletResponse response) {

try {
response.setContentType("textml;charset=UTF-8");
// 将请求、响应的编码均设置为UTF-8(防止中文乱码)
request.setCharacterEncoding("UTF-8");

response.setContentType("image/jpg");
OutputStream os = response.getOutputStream();
MakeVerifyCodeService service = MakeVerifyCodeService
.getVerifyCode(60, 20);
String code = service.getCode();
os.write(service.getImage());
logger.info("生成的验证码:" + code);
Session shiroSession = SecurityUtils.getSubject().getSession();
if (shiroSession != null) {
shiroSession.setAttribute(verifyCode, code);
}
String wx_token = request.getHeader("wx-token");
if (!StringUtils.isEmpty(wx_token)) {
RedisUtil.setString("VERIFY_" + wx_token, code, 600);
}
Cookie c = CookieTool.getCookieByName(request, "wx_token");
if (c != null) {
RedisUtil.setString("VERIFY_" + c.getValue(), code, 600);
} else {
String token = UUID.randomUUID().toString().toLowerCase()
.replace("-", "");
RedisUtil.setString("VERIFY_" + token, code, 600);
Cookie cookie = new Cookie("wx_token", token);
cookie.setPath("/");
response.addCookie(cookie);
}
os.flush();
os.close();
} catch (Exception e) {
logger.error(ExceptionHelper.getExceptionDetail(e));
}
return null;
}

/**
* 系统时间
* @return
*/
protected String sysdate() {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
}

/**
* 本系统用户注销
* @return
*/
@RequestMapping(value = "/logout")
@ResponseBody
public OpResult loginout(HttpServletRequest request, HttpServletResponse response) {

OpResult result = new OpResult();

try {
Subject subject = SecurityUtils.getSubject();

if (subject.isAuthenticated()) {
logger.info("用户" + this.getLoginUserId(request) + "在" + this.sysdate() + "注销");
subject.logout();
}
result.setStatus(true);
result.setLoginstatus(0);
} catch (Exception e) {
result.setStatus(false);
result.setMsg(e.getMessage());
}

return result;

}

/**
*
*
* @param request
*/
@RequestMapping(value = "/silencelogin")
public String loginSilence(HttpServletRequest request) {

Subject subject = SecurityUtils.getSubject();

SavedRequest savedRequest = WebUtils.getSavedRequest(request);
//获取原始请求URL
String originalUrl = savedRequest.getRequestUrl();
String contextPath = request.getContextPath();

if (originalUrl.startsWith(contextPath)) {
originalUrl = originalUrl.substring(contextPath.length(), originalUrl.length());
}

if (subject.isAuthenticated()) {

return "forward:" + originalUrl;

} else {
//未登录,提示用户先登录
if (originalUrl.contains("callback")) {

originalUrl = originalUrl.replace(originalUrl.substring(0, originalUrl.indexOf("?")), "/needlogin");

return "forward:" + originalUrl;
} else {

return "forward:/needlogin";
}

}

}

/**
* 提示用户需要登录
* @return
*/
@RequestMapping(value = "/needlogin")
@ResponseBody
public OpResult needLogin(HttpServletRequest request, HttpServletResponse response) {
OpResult op = new OpResult();

op.setStatus(true);
op.setMsg("请登录");
op.setLoginstatus(0);

if (supportJsonp(request)) {

jsonpWrapper(request, response, op);

return null;

} else {

return op;
}
}

/**
* 提示用户需要权限
* @return
*/
@RequestMapping(value = "/unauthorized")
@ResponseBody
public OpResult unauthorized(HttpServletRequest request, HttpServletResponse response) {
OpResult op = new OpResult();

op.setStatus(false);
op.setMsg("没有权限访问");
op.setLoginstatus(1);

if (supportJsonp(request)) {

jsonpWrapper(request, response, op);

return null;

} else {

return op;
}
}
}


登录认证业务例子:

比如我们配置了
/auth/**=authc
,表示所有带/auth的资源都会被拦截进行登录验证,现在zhangsan未登录首次访问资源/auth/test,shiro拦截到以后会跳转到我们配置的
<property name="loginUrl" value="/silencelogin" />
,具体看loginController。

zhangsan登录时,验证信息(自己写service判断),登录成功后,shiro会将登录信息存放到认证信息类中,并缓存起来,再次访问/auth/test时,shiro就不会拦截,直接访问资源。

注意:缓存的前提是,我们配置了

<bean id="cacheManager" class="org.apache.shiro.cache.MemoryConstrainedCacheManager" />


权限认证业务例子:

比如用户shangsan,拥有角色管理员 1,管理员对应的权限为a,b,资源(接口)/test1 对应的权限为a,/test2对应的权限为c


项目启动时会初始化资源权限信息并缓存起来, 比如/test1 对应a, /test2对应c

zhangsan登录时会查询他对应的权限,并缓存起来,比如 zhangsan对应a,b

当zhangsan访问/test1资源时,shiro会把前面两个存储的权限(a),(a,b)进行对比,zhangsan有a这个权限,就可以访问。

当shangsan访问/test2资源时,shiro会把前面两个存储的权限(c),(a,b)进行对比,zhangsan没有c这个权限,就不能访问,跳转到spring-shiro配置的
<property name="unauthorizedUrl" value="/unauthorized" />


注意:权限认证之前会先进行登录认证,就算/test1不是/auth/test1,也会进行登录验证
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  shiro spring