最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

spring boot 集成 shiro 自定義密碼驗證 自定義freemarker標(biāo)簽根據(jù)權(quán)限渲染不同頁面(推薦

 更新時間:2018年12月06日 08:36:40   作者:朋也  
這篇文章主要介紹了spring-boot 集成 shiro 自定義密碼驗證 自定義freemarker標(biāo)簽根據(jù)權(quán)限渲染不同頁面,需要的朋友可以參考下

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

引入依賴

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

用戶,角色,權(quán)限

就是經(jīng)典的RBAC權(quán)限系統(tǒng),下面簡單給一下實體類字段

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;
 // 權(quán)限的父節(jié)點的id
 private Integer pid;
 // getter setter...
}

自定義Realm

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

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

具體代碼如下

MyShiroRealm.java

@Component
public class MyShiroRealm extends AuthorizingRealm {
 private Logger log = LoggerFactory.getLogger(MyShiroRealm.class);
 @Autowired
 private AdminUserService adminUserService;
 @Autowired
 private RoleService roleService;
 @Autowired
 private PermissionService permissionService;
 // 用戶權(quán)限配置
 @Override
 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
 //訪問@RequirePermission注解的url時觸發(fā)
 SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
 AdminUser adminUser = adminUserService.selectByUsername(principals.toString());
 //獲得用戶的角色,及權(quán)限進行綁定
 Role role = roleService.selectById(adminUser.getRoleId());
 // 其實這里也可以不要權(quán)限那個類了,直接用角色這個類來做鑒權(quán),
 // 不過角色包含很多的權(quán)限,已經(jīng)算是大家約定的了,所以下面還是查詢權(quán)限然后放在AuthorizationInfo里
 simpleAuthorizationInfo.addRole(role.getName());
 // 查詢權(quán)限
 List<Permission> permissions = permissionService.selectByRoleId(adminUser.getRoleId());
 // 將權(quán)限具體值取出來組裝成一個權(quán)限String的集合
 List<String> permissionValues = permissions.stream().map(Permission::getValue).collect(Collectors.toList());
 // 將權(quán)限的String集合添加進AuthorizationInfo里,后面請求鑒權(quán)有用
 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());
 }
}

實現(xiàn)密碼校驗

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

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

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

public class MyCredentialsMatcher implements CredentialsMatcher {

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

配置shiro

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

@Configuration
public 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<>();
 // 配置不會被攔截的鏈接 順序判斷 相關(guān)靜態(tài)資源
 map.put("/static/**", "anon");
 //配置退出 過濾器,其中的具體的退出代碼Shiro已經(jīng)替我們實現(xiàn)了
 map.put("/admin/logout", "logout");
 //<!-- 過濾鏈定義,從上向下順序執(zhí)行,一般將/**放在最為下邊 -->:這是一個坑呢,一不小心代碼就不好使了;
 //<!-- authc:所有url都必須認(rèn)證通過才可以訪問; anon:所有url都都可以匿名訪問-->
 map.put("/admin/**", "authc");
 // 如果不設(shè)置默認(rèn)會自動尋找Web工程根目錄下的"/login.jsp"頁面
 shiroFilterFactoryBean.setLoginUrl("/adminlogin");
 // 登錄成功后要跳轉(zhuǎn)的鏈接
 shiroFilterFactoryBean.setSuccessUrl("/admin/index");
 //未授權(quán)界面;
 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;
 }
}

登錄

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

@PostMapping("/adminlogin")
public String adminLogin(String username, String password,
       @RequestParam(defaultValue = "0") Boolean rememberMe,
       RedirectAttributes redirectAttributes) {
 try {
 // 添加用戶認(rèn)證信息
 Subject subject = SecurityUtils.getSubject();
 if (!subject.isAuthenticated()) {
  UsernamePasswordToken token = new UsernamePasswordToken(username, password, rememberMe);
  //進行驗證,這里可以捕獲異常,然后返回對應(yīng)信息
  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");
}

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

調(diào)用 subject.login(token); 方法后,它會去鑒權(quán),期間會產(chǎn)生各種各樣的異常,有以下幾種,可以通過捕捉不同的異常然后提示頁面不同的錯誤信息,相當(dāng)?shù)姆奖阊剑心居?/p>

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

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

到這里已經(jīng)可以正常的實現(xiàn)登錄了,下面來說一些其它相關(guān)的功能的實現(xiàn)

自定freemarker標(biāo)簽

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

ShiroTag.java

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

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

FreemarkerConfig.java

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

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

<#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>

加上這個后,在渲染頁面的時候,就會根據(jù)當(dāng)前用戶是否有查看話題列表的權(quán)限,然后來渲染這個菜單

注解權(quán)限

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

在ShiroConfig里加上兩個Bean

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

有了這兩個Bean就可以用shiro的注解鑒權(quán)了,用法如下 @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 注解外,還有其它幾個鑒權(quán)的注解

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

一般 @RequiresPermissions 就夠用了

總結(jié)

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

相關(guān)文章

  • Java?Playwright實現(xiàn)瀏覽器最大化

    Java?Playwright實現(xiàn)瀏覽器最大化

    Playwright?是一個用于自動化?Web?應(yīng)用測試的現(xiàn)代工具,支持多種語言(包括?Java)及多個瀏覽器,本文主要介紹了Java如何使用Playwright實現(xiàn)瀏覽器最大化,需要的可以參考下
    2024-12-12
  • Servlet的5種方式實現(xiàn)表單提交(注冊小功能),后臺獲取表單數(shù)據(jù)實例

    Servlet的5種方式實現(xiàn)表單提交(注冊小功能),后臺獲取表單數(shù)據(jù)實例

    這篇文章主要介紹了Servlet的5種方式實現(xiàn)表單提交(注冊小功能),后臺獲取表單數(shù)據(jù)實例,非常具有實用價值,需要的朋友可以參考下
    2017-05-05
  • Java基礎(chǔ)之final關(guān)鍵字作用案例

    Java基礎(chǔ)之final關(guān)鍵字作用案例

    這篇文章主要介紹了Java基礎(chǔ)之final關(guān)鍵字作用案例,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-07-07
  • Java創(chuàng)建,編輯與刪除Excel迷你圖表的實現(xiàn)方法

    Java創(chuàng)建,編輯與刪除Excel迷你圖表的實現(xiàn)方法

    迷你圖是Excel工作表單元格中表示數(shù)據(jù)的微型圖表。本文將通過Java代碼示例介紹如何在Excel中創(chuàng)建迷你圖表,以及編輯和刪除表格中的迷你圖表,需要的可以參考一下
    2022-05-05
  • mybatis?<foreach>標(biāo)簽動態(tài)增刪改查方式

    mybatis?<foreach>標(biāo)簽動態(tài)增刪改查方式

    這篇文章主要介紹了mybatis?<foreach>標(biāo)簽動態(tài)增刪改查方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • SpringCloud使用Nacos保存和讀取變量的配置方法

    SpringCloud使用Nacos保存和讀取變量的配置方法

    在使用SpringCloud開發(fā)微服務(wù)時,經(jīng)常會遇到一些比較小的后臺參數(shù)配置,這些配置不足以單獨開一張表去存儲,而且其他服務(wù)會讀取該參數(shù),這篇文章主要介紹了SpringCloud使用Nacos保存和讀取變量,需要的朋友可以參考下
    2022-07-07
  • eclipse/intellij idea 查看java源碼和注釋方法

    eclipse/intellij idea 查看java源碼和注釋方法

    下面小編就為大家?guī)硪黄猠clipse/intellij idea 查看java源碼和注釋方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-05-05
  • SpringBoot整合Mybatis-plus實現(xiàn)多級評論功能

    SpringBoot整合Mybatis-plus實現(xiàn)多級評論功能

    本文介紹了如何使用SpringBoot整合Mybatis-plus實現(xiàn)多級評論功能,同時提供了數(shù)據(jù)庫的設(shè)計和詳細(xì)的后端代碼,前端界面使用的Vue2,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2023-05-05
  • 通過圖例了解PowerDesigner使用方法

    通過圖例了解PowerDesigner使用方法

    這篇文章主要介紹了通過圖例了解PowerDesigner使用方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-08-08
  • 基于MyBatis的parameterType傳入?yún)?shù)類型

    基于MyBatis的parameterType傳入?yún)?shù)類型

    這篇文章主要介紹了基于MyBatis的parameterType傳入?yún)?shù)類型,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09

最新評論

宜兰市| 南投县| 达尔| 万年县| 伊川县| 浦东新区| 绥宁县| 瑞安市| 楚雄市| 登封市| 神池县| 岱山县| 盐城市| 岑巩县| 忻州市| 南召县| 建水县| 年辖:市辖区| 聂拉木县| 郴州市| 江西省| 开平市| 宝应县| 北川| 杭锦后旗| 深州市| 淳化县| 广饶县| 桐乡市| 山阳县| 邵武市| 吉林省| 霍林郭勒市| 内丘县| 汾西县| 铜梁县| 赤壁市| 治多县| 盘锦市| 淳安县| 当涂县|