spring boot shiro redis整合基于角色和权限的安全管理-Java编程
一、概述
本博客主要讲解spring boot整合Apache的shiro框架,实现基于角色的安全访问控制或者基于权限的访问安全控制,其中还使用到分布式缓存redis进行用户认证信息的缓存,减少数据库查询的开销。Apache shiro与spring security的作用几乎一样都是简化了Java程序的权限控制开发。二、项目
2.1首先是通过eclipse创建一个最新的spring boot项目,并添加以下依赖:
pom.xml 复制<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>net.xqlee.project.demo.shiro</groupId>
<artifactId>demo-springboot-shiro</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>demo-springboot-shiro-hello</name>
<description>demo-springboot-shiro-hello</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- shiro权限控制框架 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.3.2</version>
</dependency>
<!--缓存暂时用简单的 https://mvnrepository.com/artifact/org.ehcache/ehcache -->
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/net.sf.json-lib/json-lib -->
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.4</version>
<classifier>jdk15</classifier>
</dependency>
<!-- redis缓存 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.2配置redis
application.properties文件中添加配置: 复制####################Redis 配置信息 ##########################
# Redis数据库分片索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=10.1.1.2
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=0
#测试redis的缓存日志
logging.level.net.xqlee.project.demo.shiro.config.shiro.cache=DEBUG
Java config的redis配置
RedisConfig
复制package net.xqlee.project.demo.shiro.config.redis;
import java.lang.reflect.Method;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
@Configuration
@EnableCaching // 继承CachingConfigurerSupport并重写方法,配合该注解实现spring缓存框架的启用
public class RedisConfig extends CachingConfigurerSupport {
/** 载入通过配置文件配置的连接工场 **/
@Autowired
RedisConnectionFactory redisConnectionFactory;
@SuppressWarnings("rawtypes")
@Autowired
RedisTemplate redisTemplate;
@Bean
RedisTemplate<String, Object> objRedisTemplate() {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
/*
* (non-Javadoc)
*
* @see org.springframework.cache.annotation.CachingConfigurerSupport#
* cacheManager()
*/
@Bean // 必须添加此注解
@Override
public CacheManager cacheManager() {
RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate);
// 设置缓存过期时间
// redisCacheManager.setDefaultExpiration(60);//秒
return redisCacheManager;
}
/**
* 重写缓存的key生成策略,可根据自身业务需要进行自己的配置生成条件
*
* @see org.springframework.cache.annotation.CachingConfigurerSupport#
* keyGenerator()
*/
@Bean // 必须项
@Override
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(method.getName());
for (Object obj : params) {
sb.append(obj.toString());
}
return sb.toString();
}
};
}
}
2.3创建shiro需要的 redis缓存器和缓存管理实现类
首先是缓存器cache的实现RedisCache.java
复制package net.xqlee.project.demo.shiro.config.shiro.cache;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
/**
* Redis的Shiro缓存对象实现
*
* @author xq
*
* @param <K>
* @param <V>
*/
public class RedisCache<K, V> implements Cache<K, V> {
private static final Logger logger = LoggerFactory.getLogger(RedisCache.class);
private RedisTemplate<K, V> redisTemplate;
private final static String PREFIX = "shiro-cache:";
private String cacheKey;
private long globExpire = 30;
@SuppressWarnings({ "rawtypes", "unchecked" })
public RedisCache(final String name, final RedisTemplate redisTemplate) {
this.cacheKey = PREFIX + name + ":";
this.redisTemplate = redisTemplate;
}
@Override
public V get(K key) throws CacheException {
logger.debug("Shiro从缓存中获取数据KEY值["+key+"]");
redisTemplate.boundValueOps(getCacheKey(key)).expire(globExpire, TimeUnit.MINUTES);
return redisTemplate.boundValueOps(getCacheKey(key)).get();
}
@Override
public V put(K key, V value) throws CacheException {
V old = get(key);
redisTemplate.boundValueOps(getCacheKey(key)).set(value);
return old;
}
@Override
public V remove(K key) throws CacheException {
V old = get(key);
redisTemplate.delete(getCacheKey(key));
return old;
}
@Override
public void clear() throws CacheException {
redisTemplate.delete(keys());
}
@Override
public int size() {
return keys().size();
}
@Override
public Set<K> keys() {
return redisTemplate.keys(getCacheKey("*"));
}
@Override
public Collection<V> values() {
Set<K> set = keys();
List<V> list = new ArrayList<>();
for (K s : set) {
list.add(get(s));
}
return list;
}
@SuppressWarnings("unchecked")
private K getCacheKey(Object k) {
return (K) (this.cacheKey + k);
}
}
还有缓存管理器:RedisCacheManager.java
复制package net.xqlee.project.demo.shiro.config.shiro.cache;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheException;
import org.apache.shiro.cache.CacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
/**
* Redis的Shiro缓存管理器实现
*
* @author xq
*
*/
public class RedisCacheManager implements CacheManager {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Override
public <K, V> Cache<K, V> getCache(String name) throws CacheException {
return new RedisCache<>(name, redisTemplate);
}
}
2.4自定义一个shiro的realm实现
创建realm之前需要编写一个模拟数据库查询的用户业务处理类,提供给上面的自定义realm使用
简单的用户登录对象: 复制package net.xqlee.project.demo.shiro.pojo;
import java.util.Date;
import java.util.List;
/**
* 用户信息
*
* @author xqlee
*
*/
public class LoginAccount {
/** 用户名 */
String loginName;
List<String> roles;// 测试用
List<String> permissions;// 测试用直接放用户登录对象里面
/** 用户密码 **/
String password;
boolean enabled;
Date createDate;
boolean isExpired;
public String getLoginName() {
return loginName;
}
public void setLoginName(String loginName) {
this.loginName = loginName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public Date getCreateDate() {
return createDate;
}
public void setCreateDate(Date createDate) {
this.createDate = createDate;
}
public boolean isExpired() {
return isExpired;
}
public void setExpired(boolean isExpired) {
this.isExpired = isExpired;
}
public List<String> getRoles() {
return roles;
}
public void setRoles(List<String> roles) {
this.roles = roles;
}
public List<String> getPermissions() {
return permissions;
}
public void setPermissions(List<String> permissions) {
this.permissions = permissions;
}
}
用户模拟业务处理
复制package net.xqlee.project.demo.shiro.service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.stereotype.Component;
import net.xqlee.project.demo.shiro.pojo.LoginAccount;
/**
* 用户业务服务类
*
* @author xqlee
*
*/
@Component("userService")
public class UserService {
/** 由于重点不在数据库,这里需要使用数据库的地方全部用map代替 **/
/** 用户信息 **/
static Map<String, LoginAccount> users = new HashMap<>();
static {
// 创建一个用户
LoginAccount account = new LoginAccount();
account.setLoginName("leftso");
account.setPassword("123456");
account.setEnabled(true);
account.setExpired(false);
// 角色添加
List<String> roles = new ArrayList<>();
roles.add("ROLE_USER");
account.setRoles(roles);
List<String> permissions = new ArrayList<>();
permissions.add("query");
permissions.add("delete");
account.setPermissions(permissions);
users.put(account.getLoginName(), account);
// 创建一个用户
LoginAccount admin = new LoginAccount();
admin.setLoginName("admin");
admin.setPassword("123456");
admin.setEnabled(true);
admin.setExpired(false);
// 角色添加
roles = new ArrayList<>();
roles.add("ROLE_ADMIN");
admin.setRoles(roles);
permissions = new ArrayList<>();
permissions.add("query");
permissions.add("delete");
admin.setPermissions(permissions);
users.put("admin", admin);
}
/**
* 通过用户名获取用户权限集合
*
* @param loginName
* 用户名
* @return 用户的权限集合
*/
public List<String> getPermissionsByLoginName(String loginName) {
if (users.containsKey(loginName)) {
return users.get(loginName).getPermissions();
}
return new ArrayList<>();
}
/**
* 通过用户名获取用户信息
*
* @param loginName
* 用户名
* @return 用户信息
*/
public LoginAccount getLoginAccountByLoginName(String loginName) {
if (users.containsKey(loginName)) {
return users.get(loginName);
}
return null;
}
}
复制package net.xqlee.project.demo.shiro.config.shiro;
import java.util.List;
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.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import net.xqlee.project.demo.shiro.pojo.LoginAccount;
import net.xqlee.project.demo.shiro.service.UserService;
/**
* 实现一个基于JDBC的Realm,继承AuthorizingRealm可以看见需要重写两个方法,doGetAuthorizationInfo和doGetAuthenticationInfo
*
* @author xqlee
*
*/
@Component("JDBCShiroRealm")
public class JDBCShiroRealm extends AuthorizingRealm {
private static final Logger logger = LoggerFactory.getLogger(JDBCShiroRealm.class);
/*** 用户业务处理类,用来查询数据库中用户相关信息 ***/
@Autowired
UserService userService;
/***
* 获取用户授权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
logger.info("##################执行Shiro权限认证##################");
// 获取用户名
String loginName = (String) principalCollection.fromRealm(getName()).iterator().next();
// 判断用户名是否存在
if (StringUtils.isEmpty(loginName)) {
return null;
}
// 查询登录用户信息
LoginAccount account = userService.getLoginAccountByLoginName(loginName);
if (account == null) {
logger.warn("用户[" + loginName + "]信息不存在");
return null;
}
// 创建一个授权对象
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// 进行权限设置
List<String> permissions = account.getPermissions();
if (permissions != null && !permissions.isEmpty()) {
info.addStringPermissions(permissions);
}
// 角色设置
List<String> roles = account.getRoles();
if (roles != null) {
info.addRoles(roles);
}
return info;
}
/**
* 获取用户认证信息
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken)
throws AuthenticationException {
logger.info("##################执行Shiro登陆认证##################");
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
// 通过表单接收的用户名
String loginName = token.getUsername();
if (loginName != null && !"".equals(loginName)) {
// 模拟数据库查询用户信息
LoginAccount account = userService.getLoginAccountByLoginName(loginName);
if (account != null) {
// 登陆的主要信息: 可以是一个实体类的对象, 但该实体类的对象一定是根据 token 的 username 查询得到的.
Object principal = token.getPrincipal();
// 创建shiro的用户认证对象
// 注意该对象的密码将会传递至后续步骤与前面登陆的subject的密码进行比对。
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(principal,
account.getPassword(), getName());
return authenticationInfo;
}
}
return null;
}
}
2.5shiro的核心配置文件
ShiroConfig.java
复制package net.xqlee.project.demo.shiro.config.shiro;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.ehcache.EhCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import net.xqlee.project.demo.shiro.config.shiro.cache.RedisCacheManager;
/***
* shiro权限管理配置
*
* @author xqlee
*
*/
@Configuration
public class ShiroConfig {
/**
* ehcache缓存方案<br/>
* 简单的缓存,后续可更换为redis缓存,通过自己实现shiro的CacheManager接口和Cache接口
*
* @return
*/
@Bean
public CacheManager shiroEhCacheManager() {
EhCacheManager cacheManager = new EhCacheManager();
cacheManager.setCacheManagerConfigFile("classpath:ehcache-shiro.xml");
return cacheManager;
}
/**
* redis缓存方案
*
* @return
*/
@Bean
public CacheManager shiroRedisCacheManager() {
return new RedisCacheManager();
}
/****
* 自定义Real
*
* @return
*/
@Bean
public JDBCShiroRealm jdbcShiroRealm() {
JDBCShiroRealm realm = new JDBCShiroRealm();
// 根据情况使用缓存器
realm.setCacheManager(shiroRedisCacheManager());//shiroEhCacheManager()
return realm;
}
/***
* 安全管理配置
*
* @return
*/
@Bean
public SecurityManager defaultWebSecurityManager() {
// DefaultSecurityManager defaultSecurityManager = new
// DefaultSecurityManager();
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();// 注意:!!!初始化成这个将会报错java.lang.IllegalArgumentException:
// SessionContext must be an HTTP compatible
// implementation.:模块化本地测试shiro的一些总结
// 配置
securityManager.setRealm(jdbcShiroRealm());
// 注意这里必须配置securityManager
SecurityUtils.setSecurityManager(securityManager);
// 根据情况选择缓存器
securityManager.setCacheManager(shiroRedisCacheManager());//shiroEhCacheManager()
return securityManager;
}
/**
* 配置shiro的拦截器链工厂,默认会拦截所有请求,并且不可配置
*
* @return
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean() {
ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
// 配置安全管理(必须)
filterFactoryBean.setSecurityManager(defaultWebSecurityManager());
// 配置登陆的地址
filterFactoryBean.setLoginUrl("/userNoLogin.do");// 未登录时候跳转URL,如果不设置默认会自动寻找Web工程根目录下的"/login.jsp"页面
filterFactoryBean.setSuccessUrl("/welcome.do");// 成功后欢迎页面
filterFactoryBean.setUnauthorizedUrl("/403.do");// 未认证页面
// 配置拦截地址和拦截器
Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();// 必须使用LinkedHashMap,因为拦截有先后顺序
// authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问
filterChainDefinitionMap.put("/userNoLogin.do*", "anon");// 未登录跳转页面不设权限认证
filterChainDefinitionMap.put("/login.do*", "anon");// 登录接口不设置权限认真
filterChainDefinitionMap.put("/logout.do*", "anon");// 登出不需要认证
// 以下配置同样可以通过注解
// @RequiresPermissions("user:edit")来配置访问权限和角色注解@RequiresRoles(value={"ROLE_USER"})方式定义
// 权限配置示例,这里的配置理论来自数据库查询
filterChainDefinitionMap.put("/user/**", "roles[ROLE_USER],perms[query]");// /user/下面的需要ROLE_USER角色或者query权限才能访问
filterChainDefinitionMap.put("/admin/**", "perms[ROLE_ADMIN]");// /admin/下面的所有需要ROLE_ADMIN的角色才能访问
// 剩下的其他资源地址全部需要用户认证后才能访问
filterChainDefinitionMap.put("/**", "authc");
filterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
// 全部配置
// anon org.apache.shiro.web.filter.authc.AnonymousFilter 匿名访问
//
// authc org.apache.shiro.web.filter.authc.FormAuthenticationFilter
// 需要登录,不需要权限和角色可访问
//
// authcBasic
// org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter
//
// perms
// org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter
// 需要给定的权限值才能访问
//
// port org.apache.shiro.web.filter.authz.PortFilter
//
// rest org.apache.shiro.web.filter.authz.HttpMethodPermissionFilter
//
// roles org.apache.shiro.web.filter.authz.RolesAuthorizationFilter
// 需要给定的角色才能访问
//
// ssl org.apache.shiro.web.filter.authz.SslFilter
//
// user org.apache.shiro.web.filter.authc.UserFilter
//
// logout org.apache.shiro.web.filter.authc.LogoutFilter
return filterFactoryBean;
}
}
上面配置中有两个缓存器可以选择,一个是简单的ehcache,一个是redis,大型项目推荐使用redis
2.6编写一个测试的controller
复制package net.xqlee.project.demo.shiro.controller;
import javax.servlet.http.HttpServletResponse;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import net.sf.json.JSONObject;
/**
* 用户登录用
*
* @author xqlee
*
*/
@RestController
public class LoginController {
private static final Logger logger = LoggerFactory.getLogger(LoginController.class);
/****
* 用户未登录
*
* @return
*/
@GetMapping("userNoLogin.do")
public Object noLogin() {
JSONObject object = new JSONObject();
object.put("message", "用户未登录");
return object;
}
@GetMapping(value = "/login.do")
public String login(String loginName, String password) {
try {
// 创建shiro需要的token
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(loginName, password.toCharArray());
usernamePasswordToken.setRememberMe(true);// 记住
try {
SecurityUtils.getSubject().login(usernamePasswordToken);
} catch (UnknownAccountException uae) {
logger.info("对用户[" + loginName + "]进行登录验证..验证未通过,未知账户");
return "对用户[" + loginName + "]进行登录验证..验证未通过,未知账户";
} catch (IncorrectCredentialsException ice) {
logger.info("对用户[" + loginName + "]进行登录验证..验证未通过,错误的凭证");
ice.printStackTrace();
return "对用户[" + loginName + "]进行登录验证..验证未通过,错误的凭证";
} catch (LockedAccountException lae) {
logger.info("对用户[" + loginName + "]进行登录验证..验证未通过,账户已锁定");
return "对用户[" + loginName + "]进行登录验证..验证未通过,账户已锁定";
} catch (ExcessiveAttemptsException eae) {
logger.info("对用户[" + loginName + "]进行登录验证..验证未通过,错误次数过多");
return "对用户[" + loginName + "]进行登录验证..验证未通过,错误次数过多";
} catch (AuthenticationException ae) {
// 通过处理Shiro的运行时AuthenticationException就可以控制用户登录失败或密码错误时的情景
logger.info("对用户[" + loginName + "]进行登录验证..验证未通过,堆栈轨迹如下");
ae.printStackTrace();
return "用户名或密码不正确";
}
return "Login Success!";
} catch (Exception e) {
return "登陆时候发生异常," + e.getMessage();
}
}
@GetMapping("/user/hello.do")
public String hello() {
return "Hello User, From Server";
}
@GetMapping("/admin/hello.do")
public String helloAdmin() {
return "Hello Admin, From Server";
}
@GetMapping("/welcome.do")
public String loginSuccess() {
return "welcome";
}
@GetMapping("/403.do")
public Object error403(HttpServletResponse response) {
response.setStatus(403);
JSONObject object = new JSONObject();
object.put("message", "用户权限不够");
return object;
}
}
2.7测试
1.通过spring bootapplication方式启动项目,项目默认监听在8080端口首先访问需要权限的url http://localhost:8080/user/hello.do
![](https://img2018.cnblogs.com/blog/48590/201905/48590-20190504201755445-774889021.jpg)
可以看到返回的是用户未登录提示,也就是我们在controller中写的未登录时候调用的方法返回值。
现在我们先通过用户名leftso和密码123456进行登录 localhost:8080/login.do?loginName=leftso&password=123456
![](https://img2018.cnblogs.com/blog/48590/201905/48590-20190504201806119-2109392463.png)
可以看到登录成功,这个时候我们再次打开最初访问的:http://localhost:8080/user/hello.do
![](https://img2018.cnblogs.com/blog/48590/201905/48590-20190504201816589-1465378384.png)
可以看到这次我们成功访问了需要ROLE_USER角色的url,
现在用这个登录的信息访问需要ROLE_ADMIN权限的地址http://localhost:8080/admin/hello.do
![](https://img2018.cnblogs.com/blog/48590/201905/48590-20190504201827210-115780540.png)
上面可以看到,无法访问,返回的提示是权限不足
切换为admin的用户登录,然后访问地址http://localhost:8080/admin/hello.do
首先是admin登录
![](https://img2018.cnblogs.com/blog/48590/201905/48590-20190504201839620-1131864941.png)
然后访问admin权限的地址
![](https://img2018.cnblogs.com/blog/48590/201905/48590-20190504201850455-51169208.png)
上面看到admin也对应的访问成功了。
并且,切回eclipse的控制台可以看到:
![redis](http://www.leftso.com/resources/assist/images/blog/df831fee0fb74c02aece3421c25b8949.png)
我们的redis缓存也起作用了。
至此spring boot shiro redis的整合角色和权限控制讲解完毕。