1、概述
本博客主要講解spring boot整合Apache的shiro框架,實現基於角色的安全訪問控制或者基於權限的訪問安全控制,其中還使用到分佈式緩存redis進行用戶認證信息的緩存,減小數據庫查詢的開銷。Apache shiro與spring security的做用幾乎同樣都是簡化了Java程序的權限控制開發。2、項目
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

能夠看到返回的是用戶未登陸提示,也就是咱們在controller中寫的未登陸時候調用的方法返回值。
如今咱們先經過用戶名leftso和密碼123456進行登陸 localhost:8080/login.do?loginName=leftso&password=123456

能夠看到登陸成功,這個時候咱們再次打開最初訪問的:http://localhost:8080/user/hello.do

能夠看到此次咱們成功訪問了須要ROLE_USER角色的url,
如今用這個登陸的信息訪問須要ROLE_ADMIN權限的地址http://localhost:8080/admin/hello.do

上面能夠看到,沒法訪問,返回的提示是權限不足
切換爲admin的用戶登陸,而後訪問地址http://localhost:8080/admin/hello.do
首先是admin登陸

而後訪問admin權限的地址

上面看到admin也對應的訪問成功了。
而且,切回eclipse的控制檯能夠看到:
