亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > Java > 正文

spring boot 集成 shiro 自定義密碼驗證 自定義freemarker標簽根據權限渲染不同頁面

2024-07-14 08:43:01
字體:
來源:轉載
供稿:網友

項目里一直用的是 spring-security ,不得不說,spring-security 真是東西太多了,學習難度太大(可能我比較菜),這篇博客來總結一下折騰shiro的成果,分享給大家,強烈推薦shiro,真心簡單 : )

引入依賴

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

用戶,角色,權限

就是經典的RBAC權限系統,下面簡單給一下實體類字段

AdminUser.java

public class AdminUser implements Serializable { private static final long serialVersionUID = 8264158018518861440L; private Integer id; private String username; private String password; private Integer roleId; // getter setter...}

Role.java

public class Role implements Serializable { private static final long serialVersionUID = 7824693669858106664L; private Integer id; private String name; // getter setter...}

Permission.java

public class Permission implements Serializable { private static final long serialVersionUID = -2694960432845360318L; private Integer id; private String name; private String value; // 權限的父節點的id private Integer pid; // getter setter...}

自定義Realm

這貨就是查詢用戶的信息然后放在shiro的個人用戶對象的緩存里,shiro自己有一個session的對象(不是servlet里的session)作用就是后面用戶發起請求的時候拿來判斷有沒有權限

另一個作用是查詢一下用戶的信息,將用戶名,密碼組裝成一個 AuthenticationInfo 用于后面密碼校驗的

具體代碼如下

MyShiroRealm.java

 

@Componentpublic class MyShiroRealm extends AuthorizingRealm { private Logger log = LoggerFactory.getLogger(MyShiroRealm.class); @Autowired private AdminUserService adminUserService; @Autowired private RoleService roleService; @Autowired private PermissionService permissionService; // 用戶權限配置 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { //訪問@RequirePermission注解的url時觸發 SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); AdminUser adminUser = adminUserService.selectByUsername(principals.toString()); //獲得用戶的角色,及權限進行綁定 Role role = roleService.selectById(adminUser.getRoleId()); // 其實這里也可以不要權限那個類了,直接用角色這個類來做鑒權, // 不過角色包含很多的權限,已經算是大家約定的了,所以下面還是查詢權限然后放在AuthorizationInfo里 simpleAuthorizationInfo.addRole(role.getName()); // 查詢權限 List<Permission> permissions = permissionService.selectByRoleId(adminUser.getRoleId()); // 將權限具體值取出來組裝成一個權限String的集合 List<String> permissionValues = permissions.stream().map(Permission::getValue).collect(Collectors.toList()); // 將權限的String集合添加進AuthorizationInfo里,后面請求鑒權有用 simpleAuthorizationInfo.addStringPermissions(permissionValues); return simpleAuthorizationInfo; } // 組裝用戶信息 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String username = (String) token.getPrincipal(); log.info("用戶:{} 正在登錄...", username); AdminUser adminUser = adminUserService.selectByUsername(username); // 如果用戶不存在,則拋出未知用戶的異常 if (adminUser == null) throw new UnknownAccountException(); return new SimpleAuthenticationInfo(username, adminUser.getPassword(), getName()); }}

實現密碼校驗

shiro內置了幾個密碼校驗的類,有 Md5CredentialsMatcher Sha1CredentialsMatcher , 不過從1.1版本開始,都開始使用 HashedCredentialsMatcher 這個類了,通過配置加密規則來校驗

它們都實現了一個接口 CredentialsMatcher 我這里也實現這個接口,實現一個自己的密碼校驗

說明一下,我這里用的加密方式是Spring-Security里的 BCryptPasswordEncoder 作的加密,之所以用它,是因為同一個密碼被這貨加密后,密文都不一樣,下面是具體代碼

public class MyCredentialsMatcher implements CredentialsMatcher { @Override public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { // 大坑!?。。。。。。。。。。。。。。。。?! // 明明token跟info兩個對象的里的Credentials類型都是Object,斷點看到的類型都是 char[] // 但是?。。。?! token里轉成String要先強轉成 char[] // 而info里取Credentials就可以直接使用 String.valueOf() 轉成String // 醉了。。 String rawPassword = String.valueOf((char[]) token.getCredentials()); String encodedPassword = String.valueOf(info.getCredentials()); return new BCryptPasswordEncoder().matches(rawPassword, encodedPassword); }}

配置shiro

因為項目是spring-boot開發的,shiro就用java代碼配置,不用xml配置, 具體配置如下

 

@Configurationpublic class ShiroConfig { private Logger log = LoggerFactory.getLogger(ShiroConfig.class); @Autowired private MyShiroRealm myShiroRealm; @Bean public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { log.info("開始配置shiroFilter..."); ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); //攔截器. Map<String,String> map = new HashMap<>(); // 配置不會被攔截的鏈接 順序判斷 相關靜態資源 map.put("/static/**", "anon"); //配置退出 過濾器,其中的具體的退出代碼Shiro已經替我們實現了 map.put("/admin/logout", "logout"); //<!-- 過濾鏈定義,從上向下順序執行,一般將/**放在最為下邊 -->:這是一個坑呢,一不小心代碼就不好使了; //<!-- authc:所有url都必須認證通過才可以訪問; anon:所有url都都可以匿名訪問--> map.put("/admin/**", "authc"); // 如果不設置默認會自動尋找Web工程根目錄下的"/login.jsp"頁面 shiroFilterFactoryBean.setLoginUrl("/adminlogin"); // 登錄成功后要跳轉的鏈接 shiroFilterFactoryBean.setSuccessUrl("/admin/index"); //未授權界面; shiroFilterFactoryBean.setUnauthorizedUrl("/error"); shiroFilterFactoryBean.setFilterChainDefinitionMap(map); return shiroFilterFactoryBean; } // 配置加密方式 // 配置了一下,這貨就是驗證不過,,改成手動驗證算了,以后換加密方式也方便 @Bean public MyCredentialsMatcher myCredentialsMatcher() { return new MyCredentialsMatcher(); } // 安全管理器配置 @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); myShiroRealm.setCredentialsMatcher(myCredentialsMatcher()); securityManager.setRealm(myShiroRealm); return securityManager; }}

登錄

都配置好了,就可以發起登錄請求做測試了,一個簡單的表單即可,寫在Controller里就行

 

@PostMapping("/adminlogin")public String adminLogin(String username, String password,       @RequestParam(defaultValue = "0") Boolean rememberMe,       RedirectAttributes redirectAttributes) { try { // 添加用戶認證信息 Subject subject = SecurityUtils.getSubject(); if (!subject.isAuthenticated()) {  UsernamePasswordToken token = new UsernamePasswordToken(username, password, rememberMe);  //進行驗證,這里可以捕獲異常,然后返回對應信息  subject.login(token); } } catch (AuthenticationException e) { // e.printStackTrace(); log.error(e.getMessage()); redirectAttributes.addFlashAttribute("error", "用戶名或密碼錯誤"); redirectAttributes.addFlashAttribute("username", username); return redirect("/adminlogin"); } return redirect("/admin/index");}

從上面代碼可以看出,記住我功能也直接都實現好了,只需要在組裝 UsernamePasswordToken 的時候,將記住我字段傳進去就可以了,值是 true, false, 如果是true,登錄成功后,shiro會在本地寫一個cookie

調用 subject.login(token); 方法后,它會去鑒權,期間會產生各種各樣的異常,有以下幾種,可以通過捕捉不同的異常然后提示頁面不同的錯誤信息,相當的方便呀,有木有

  • AccountException 帳戶異常
  • ConcurrentAccessException 這個好像是并發異常
  • CredentialsException 密碼校驗異常
  • DisabledAccountException 帳戶被禁異常
  • ExcessiveAttemptsException 嘗試登錄次數過多異常
  • ExpiredCredentialsException 認證信息過期異常
  • IncorrectCredentialsException 密碼不正確異常
  • LockedAccountException 帳戶被鎖定異常
  • UnknownAccountException 未知帳戶異常
  • UnsupportedTokenException login(AuthenticationToken) 這個方法只能接收 AuthenticationToken 類的對象,如果傳的是其它的類,就拋這個異常

上面這么多異常,shiro在處理登錄的邏輯時,會自動的發出一些異常,當然你也可以手動去處理登錄流程,然后根據不同的問題拋出不同的異常,手動處理的地方就在自己寫的 MyShiroRealm 里的 doGetAuthenticationInfo() 方法里,我在上面代碼里只處理了一個帳戶不存在時拋出了一個 UnknownAccountException 的異常,其實還可以加更多其它的異常,這個要看個人系統的需求來定了

到這里已經可以正常的實現登錄了,下面來說一些其它相關的功能的實現

自定freemarker標簽

開發項目肯定要用到頁面模板,我這里用的是 freemarker ,一個用戶登錄后,頁面可能要根據用戶的不同權限渲染不同的菜單,github上有個開源的庫,也是可以用的,不過我覺得那個太麻煩了,就自己實現了一個,幾行代碼就能搞定

ShiroTag.java

 

@Componentpublic class ShiroTag { // 判斷當前用戶是否已經登錄認證過 public boolean isAuthenticated(){ return SecurityUtils.getSubject().isAuthenticated(); } // 獲取當前用戶的用戶名 public String getPrincipal() { return (String) SecurityUtils.getSubject().getPrincipal(); } // 判斷用戶是否有 xx 角色 public boolean hasRole(String name) { return SecurityUtils.getSubject().hasRole(name); } // 判斷用戶是否有 xx 權限 public boolean hasPermission(String name) { return !StringUtils.isEmpty(name) && SecurityUtils.getSubject().isPermitted(name); }}

將這個類注冊到freemarker的全局變量里

FreemarkerConfig.java

@Configurationpublic class FreemarkerConfig { private Logger log = LoggerFactory.getLogger(FreeMarkerConfig.class); @Autowired private ShiroTag shiroTag; @PostConstruct public void setSharedVariable() throws TemplateModelException { //注入全局配置到freemarker log.info("開始配置freemarker全局變量..."); // shiro鑒權 configuration.setSharedVariable("sec", shiroTag); log.info("freemarker自定義標簽配置完成!"); }}

有了這些配置后,就可以在頁面里使用了,具體用法如下

<#if sec.hasPermission("topic:list")> <li <#if page_tab=='topic'>class="active"</#if>> <a href="/admin/topic/list" rel="external nofollow" >  <i class="fa fa-list"></i>  <span>話題列表</span> </a> </li></#if>

加上這個后,在渲染頁面的時候,就會根據當前用戶是否有查看話題列表的權限,然后來渲染這個菜單

注解權限

有了上面freemarker標簽判斷是否有權限來渲染頁面,這樣做只能防君子,不能防小人,如果一個人知道后臺的某個訪問鏈接,但這個鏈接它是沒有權限訪問的,那他只要手動輸入這個鏈接就還是可以訪問的,所以這里還要在Controller層加一套防御,具體配置如下

在ShiroConfig里加上兩個Bean

//加入注解的使用,不加入這個注解不生效@Beanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor;}@Bean@ConditionalOnMissingBeanpublic DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator defaultAAP = new DefaultAdvisorAutoProxyCreator(); defaultAAP.setProxyTargetClass(true); return defaultAAP;}

有了這兩個Bean就可以用shiro的注解鑒權了,用法如下 @RequiresPermissions("topic:list")

 

@Controller@RequestMapping("/admin/topic")public class TopicAdminController extends BaseAdminController { @RequiresPermissions("topic:list") @GetMapping("/list") public String list() { // TODO return "admin/topic/list"; }}

shiro除了 @RequiresPermissions 注解外,還有其它幾個鑒權的注解

  • @RequiresPermissions
  • @RequiresRoles
  • @RequiresUser
  • @RequiresGuest
  • @RequiresAuthentication

一般 @RequiresPermissions 就夠用了

總結

spring-boot 集成 shiro 到這就結束了,是不是網上能找到的教程里最全的!相比 spring-security 要簡單太多了,強烈推薦


注:相關教程知識閱讀請移步到JAVA教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
日韩综合视频在线观看| 国产精品av在线| 日韩成人在线免费观看| 久久久精品久久久久| 狠狠干狠狠久久| 91亚洲一区精品| 亚洲精品成a人在线观看| 国产日韩欧美在线| 亚洲精品第一国产综合精品| 久久精品视频va| 久久夜色精品国产亚洲aⅴ| 色老头一区二区三区在线观看| 日韩av资源在线播放| 成人在线视频网站| 日韩va亚洲va欧洲va国产| 欧美精品久久久久| 国产精品视频yy9099| 亚洲福利影片在线| 久久人人爽人人爽人人片av高清| 亚洲免费视频观看| 97免费在线视频| 欧美国产一区二区三区| 久久好看免费视频| 91精品国产91久久久久久不卡| 日韩电影在线观看永久视频免费网站| 一区二区三区黄色| 亚洲人精品午夜在线观看| 亚洲一区二区中文| 国产精品www色诱视频| 国产精品96久久久久久| 欧美大尺度激情区在线播放| 国产精品高清免费在线观看| www.久久撸.com| 国产精品免费电影| 91久久国产精品| 97在线视频免费| 欧美在线亚洲一区| 久久久精品国产一区二区| 精品久久久久久亚洲精品| 91色琪琪电影亚洲精品久久| 日韩精品在线免费观看| 国产精品视频男人的天堂| 欧美日韩在线免费观看| 国产第一区电影| 亚洲精品视频免费| 久久在线观看视频| 亚洲精品美女视频| 日韩av在线播放资源| 精品久久久久久久中文字幕| 九九热r在线视频精品| 国产91成人video| 91久热免费在线视频| 性欧美亚洲xxxx乳在线观看| 久久中文字幕国产| 欧美激情亚洲国产| 欧美激情二区三区| 国产精品视频不卡| 欧美性猛交xxxx久久久| 91在线观看免费观看| 亚洲欧美综合v| 97久久精品人人澡人人爽缅北| 欧美一区二区大胆人体摄影专业网站| 亚洲一级免费视频| 欧美视频一二三| 欧美精品在线免费观看| www欧美xxxx| 九九视频直播综合网| 欧美日韩一区二区三区| 91精品久久久久| 色多多国产成人永久免费网站| 久久伊人精品一区二区三区| 亚洲国产91精品在线观看| 69av成年福利视频| 高清欧美性猛交| 欧美日韩国产限制| 欧美精品18videos性欧| 亚洲人a成www在线影院| 欧美黑人巨大精品一区二区| 欧美极品少妇xxxxⅹ喷水| 国产一区二区三区在线观看网站| 在线成人激情黄色| 亚洲精品黄网在线观看| 欧美夜福利tv在线| 2024亚洲男人天堂| 欧美中文字幕精品| 国产一区二区免费| 欧美性生交xxxxxdddd| 午夜精品蜜臀一区二区三区免费| 91在线观看免费网站| 欧美性在线观看| 国产精品视频xxx| 亚洲老板91色精品久久| 欧美黄色片免费观看| 欧美巨猛xxxx猛交黑人97人| 国产精品老女人视频| 亚洲精品小视频在线观看| 日韩国产精品亚洲а∨天堂免| 亚洲第一网站男人都懂| 欧美精品www在线观看| 亚洲专区中文字幕| 在线国产精品播放| 欧洲亚洲女同hd| 欧洲精品毛片网站| 久久久久久久电影一区| 午夜精品久久久久久99热| 亚洲国产天堂久久综合| 在线视频欧美性高潮| 久久久久亚洲精品| 欧美成人精品在线观看| 97国产suv精品一区二区62| 亚洲风情亚aⅴ在线发布| 欧美日韩另类字幕中文| 日韩中文字幕免费视频| 成人激情免费在线| 最新中文字幕亚洲| 欧美一级淫片丝袜脚交| 亚洲字幕在线观看| 亚洲精品国产欧美| 欧美国产在线电影| 91av网站在线播放| 欧美久久精品一级黑人c片| 亚洲成人999| 日韩一级黄色av| 91精品在线观| 欧美疯狂性受xxxxx另类| 欧美日韩性视频| 亚洲一品av免费观看| 久久精品福利视频| 亚洲国产成人精品电影| 国产成人精品久久久| 国内伊人久久久久久网站视频| 欧美视频一二三| 国产一区二中文字幕在线看| 亚洲欧美日韩一区在线| 亚洲va欧美va国产综合剧情| 久久久久久91香蕉国产| 久久人体大胆视频| 欧美极品少妇xxxxⅹ喷水| 亚洲精品v天堂中文字幕| 伊人久久精品视频| 国产精品色午夜在线观看| 不用播放器成人网| 中文字幕亚洲在线| 亚洲精品二三区| 国产香蕉97碰碰久久人人| 欧美激情第6页| 日韩小视频在线观看| 欧美日韩中国免费专区在线看| 国产97在线观看| 亚洲国产欧美一区二区丝袜黑人| 国语自产在线不卡| 亚洲老头同性xxxxx| 欧美自拍视频在线观看| 91免费综合在线| 国产欧美在线视频| www高清在线视频日韩欧美| 欧美在线日韩在线| 日韩成人在线观看| 国产日本欧美一区| 国产成人精品在线观看| 亚洲免费电影在线观看| 国产成人综合一区二区三区| 亚洲成成品网站| 777午夜精品福利在线观看|