Springboot2(23)輕鬆整合shiro(帶驗證碼)
阿新 • • 發佈:2018-12-29
Shiro配置
1.Spring整合Shiro一般通過xml配置,SpringBoot整合Shiro一般通過java程式碼配合@Configuration和@Bean配置。
2.Shiro的核心通過過濾器Filter實現。Shiro中的Filter是通過URL規則來進行過濾和許可權校驗,所以我們需要定義一系列關於URL的規則和訪問許可權。
3.SpringBoot整合Shiro,我們需要寫的主要是兩個類,ShiroConfiguration類,還有繼承了AuthorizingRealm的Realm類。
- ShiroConfiguration類,用來配置Shiro,注入各種Bean。包括過濾器(shiroFilter)、安全事務管理器(SecurityManager)、密碼憑證(CredentialsMatcher)、aop註解支援(authorizationAttributeSourceAdvisor)等等
- Realm類,包括登陸認證(doGetAuthenticationInfo)、授權認證(doGetAuthorizationInfo)
引入依賴
<properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> <shiro.version>1.4.0</shiro.version> <commons.lang.version>2.6</commons.lang.version> <kaptcha.version>0.0.9</kaptcha.version> </properties> <dependencies> <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>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>${commons.lang.version}</version> </dependency> <dependency> <groupId>com.github.axet</groupId> <artifactId>kaptcha</artifactId> <version>${kaptcha.version}</version> </dependency> </dependencies>
編寫ShiroConfiguration類
/**
* Shiro的配置檔案
*/
@Configuration
public class ShiroConfig {
/**
* Session會話管理器,session交給shiro管理
*/
@Bean
public DefaultWebSessionManager sessionManager(@Value("${myframe.sessionTimeout:3600}") long globalSessionTimeout){
DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
//是否開啟會話驗證器,預設是開啟的
sessionManager.setSessionValidationSchedulerEnabled(true);
//禁止URL地標後面新增JSESSIONID
sessionManager.setSessionIdUrlRewritingEnabled(false);
//設定全域性會話超時時間
sessionManager.setGlobalSessionTimeout(globalSessionTimeout * 1000);
sessionManager.setSessionValidationInterval(globalSessionTimeout * 1000);
return sessionManager;
}
/**
* 安全管理器
* @param userRealm
* @param sessionManager
* @return
*/
@Bean("securityManager")
public SecurityManager securityManager(UserRealm userRealm, SessionManager sessionManager) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(userRealm);
securityManager.setRememberMeManager(null);
securityManager.setSessionManager(sessionManager);
return securityManager;
}
@Bean("shiroFilter")
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
shiroFilter.setSecurityManager(securityManager);
shiroFilter.setLoginUrl("/login.html");
shiroFilter.setUnauthorizedUrl("/");
Map<String, String> filterMap = new LinkedHashMap<>();
filterMap.put("/public/**", "anon");
filterMap.put("/login.html", "anon");
filterMap.put("/sys/login", "anon");
filterMap.put("/captcha.jpg", "anon");
filterMap.put("/**", "authc");
shiroFilter.setFilterChainDefinitionMap(filterMap);
return shiroFilter;
}
/**
* Shiro生命週期處理器,保證實現了Shiro內部lifecycle函式的bean執行
* @return
*/
@Bean("lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
/**
* 啟用shrio授權註解攔截方式,AOP式方法級許可權檢查
* @return
*/
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
proxyCreator.setProxyTargetClass(true);
return proxyCreator;
}
/**
* 加入註解的使用,不加入這個註解不生效 使用shiro框架提供的切面類,用於建立代理物件
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}
過濾器Filter實現
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean shiroFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new DelegatingFilterProxy("shiroFilter"));
//該值預設為false,表示生命週期由SpringApplicationContext管理,設定為true則表示由ServletContainer管理
registration.addInitParameter("targetFilterLifecycle", "true");
registration.setOrder(Integer.MAX_VALUE - 1);
registration.setEnabled(true);
registration.addUrlPatterns("/*");
return registration;
}
}
Realm類實現
/**
* 認證
*/
@Component
public class UserRealm extends AuthorizingRealm {
@Resource
private SysUserDao sysUserDao;
/**
* 授權(驗證許可權時呼叫)
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
SysUserEntity user = (SysUserEntity)principals.getPrimaryPrincipal();
Long userId = user.getUserId();
//使用者許可權列表
Set<String> permsSet = new HashSet<>();
String perms = sysUserDao.queryAllPerms(userId);
permsSet.addAll(Arrays.asList(perms.trim().split(",")));
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setStringPermissions(permsSet);
return info;
}
/**
* 認證(登入時呼叫)
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(
AuthenticationToken authcToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken)authcToken;
//查詢使用者資訊
SysUserEntity user = sysUserDao.selectOne(token.getUsername());
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, user.getPassword(), getName());
return info;
}
}
登入實現類
**
* 登入相關
*/
@Controller
public class SysLoginController {
@Autowired
private Producer producer;
/**
* 生成驗證碼
* @param response
* @throws IOException
*/
@RequestMapping("captcha.jpg")
public void captcha(HttpServletResponse response)throws IOException {
response.setHeader("Cache-Control", "no-store, no-cache");
response.setContentType("image/jpeg");
//生成文字驗證碼
String text = producer.createText();
//生成圖片驗證碼
BufferedImage image = producer.createImage(text);
//儲存到shiro session
ShiroUtils.setSessionAttribute(Constants.KAPTCHA_SESSION_KEY, text);
ServletOutputStream out = response.getOutputStream();
ImageIO.write(image, "jpg", out);
}
/**
* 登入
*/
@ResponseBody
@RequestMapping(value = "/sys/login", method = RequestMethod.POST)
public R login(String username, String password, String captcha) throws IllegalAccessException {
String kaptcha = ShiroUtils.getKaptcha(Constants.KAPTCHA_SESSION_KEY);
if(!captcha.equalsIgnoreCase(kaptcha)){
return R.error("驗證碼不正確");
}
try{
Subject subject = ShiroUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
subject.login(token);
}catch (UnknownAccountException e) {
return R.error(e.getMessage());
}catch (LockedAccountException e) {
return R.error("賬號已被鎖定,請聯絡管理員");
} catch (IncorrectCredentialsException e) {
return R.error("賬號或密碼不正確");
}catch (AuthenticationException e) {
return R.error("賬戶驗證失敗");
}
return R.ok();
}
/**
* 退出
*/
@RequestMapping(value = "logout", method = RequestMethod.GET)
public String logout() {
ShiroUtils.logout();
return "redirect:login.html";
}
}
許可權測試類
@RestController
public class PermController {
@RequestMapping("/sysRead")
@RequiresPermissions("sys:read")
public String sysRead(){
return "you can sysRead";
}
@RequiresPermissions("sys:write")
@RequestMapping("/sysWrite")
public String sysWrite(){
return "you can sysWrite";
}
@RequiresPermissions("sys:delete")
@RequestMapping("/sysDelete")
public String sysDel(){
return "you can sysDelete";
}
}
每次訪問帶有@RequiresPermissions()方法時,都會進入UserRealm類的AuthorizationInfo()授權方法
另外subject.hasRole(“admin”) 或 subject.isPermitted(“admin”) 也會呼叫AuthorizationInfo()授權方法
登入測試(使用者密碼tom/123456)
許可權測試