Securing Web Applications with Apache Shiro(2)

news/2024/9/5 19:31:19 标签: ui, git, runtime

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

Step 3: Enable Login and Logout

Now we have users, and we can add, remove and disable them easily in a UI. Now we can start enabling features like login/logout and access control in our application.

Perform the following git checkout command to load the step3 branch:

git checkout step3

This checkout will load the following 2 additions:

  • A new src/main/webapp/login.jsp file has been added with a simple login form. We’ll use that to login.

  • The shiro.ini file has been updated to support web (URL)-specific capabilities.

Step 3a: Enable Shiro form login and logout support

The step3 branch’s src/main/webapp/WEB-INF/shiro.ini file contains the following 2 additions:

[main]

shiro.loginUrl = /login.jsp

# Stuff we've configured here previously is omitted for brevity

[urls]
/login.jsp = authc
/logout = logout

shiro.* lines

At the top of the [main] section, there is a new line:

shiro.loginUrl = /login.jsp

This is a special configuration directive that tells Shiro “For any of Shiro’s default filters that have a loginUrl property, I want that property value to be set to /login.jsp.”

This allows Shiro’s default authc filter (by default, a FormAuthenticationFilter) to know about the login page. This is necessary for theFormAuthenticationFilter to work correctly.

The [urls] section

The [urls] section is a new web-specific INI section.

This section allows you to use a very succinct name/value pair syntax to tell shiro how to filter request for any given URL path. All paths in[urls] are relative to the web application’s [HttpServletRequest.getContextPath()](http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/servlet/http/HttpServletRequest.html#getContextPath()) value.

These name/value pairs offer an extremely powerful way to filter requests, allowing for all sorts of security rules. A deeper coverage of urls and filter chains is outside the scope of this document, but please do read more about it if you’re interested.

For now, we’ll cover the two lines that were added:

/login.jsp = authc
/logout = logout
  • The first line indicates “Whenever Shiro sees a request to the /login.jsp url, enable the Shiro authc filter during the request”.

  • The second line means “whenever Shiro sees a request to the /logout url, enable the Shiro logout filter during the request.”

Both of these filters are a little special: they don’t actually require anything to be ‘behind’ them. Instead of filtering, they will actually just process the request entirely. This means there isn’t anything for you to do for requests to these URLs - no controllers to write! Shiro will handle the requests as necessary.

Step 3b: Add a login page

Since Step 3a enabled login and logout support, now we need to ensure there is actually a /login.jsp page to display a login form.

The step3 branch contains a new src/main/webapp/login.jsp page. This is a simple enough bootstrap-themed HTML login page, but there are four important things in it:

  1. The form’s action value is the empty string. When a form does not have an action value, the browser will submit the form request to the same URL. This is fine, as we will tell Shiro what that URL is shortly so Shiro can automatically process any login submissions. The/login.jsp = authc line in shiro.ini is what tells the authc filter to process the submission.

  2. There is a username form field. The Shiro authc filter will automatically look for a username request parameter during login submission and use that as the value during login (many Realms allow this to be an email or a username).

  3. There is a password form field. The Shiro authc filter will automatically look for a password request parameter during login submission.

  4. There is a rememberMe checkbox whose ‘checked’ state can be a ‘truthy’ value (truet1enabledyyes, or on).

Our login.jsp form just uses the default usernamepassword, and rememberMe form field names. They naems are configurable if you wish to change them - see the FormAuthenticationFilter JavaDoc for information.

Step 3c: Run the webapp

After making the changes as specified in Step 2b and 2c, go ahead and run the web app:

$ mvn jetty:run

Step 3d: Try to Login

With your web browser, navigate to localhost:8080/login.jsp and you will see our new shiny login form.

Enter in a username and password of the account you created at the end of Step 2, and hit ‘Login’. If the login is successful, you will be directed to the home page! If the login fails, you will be shown the login page again.

Tip: If you want a successful login to redirect the user to a different page other than the home page (context path /), you can set theauthc.successUrl = /whatever in the INI’s [main] section.

Hit ctl-C (or cmd-C on a mac) to shut down the web app.

Step 4: User-specific UI changes

It’s usually a requirement to change a web user interface based on who the user is. We can do that easily because Shiro supports a JSP tag library to do things based on the currently logged-in Subject (user).

Perform the following git checkout command to load the step4 branch:

git checkout step4

This step makes some additions to our home.jsp page:

  • When the current user viewing the page is not logged in, they will see a ‘Welcome Guest’ message and see the link to the login page.

  • When the current user viewing the page is logged in, they will see their own name, ‘Welcome username’ and a link to log out.

This type of UI customization is very common for a navigation bar, with user controls on the upper right of the screen.

Step 4a: Add the Shiro Tag Library Declaration

The home.jsp file was modified to include two lines at the top:

<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

These two JSP page directives allow the Core (c:) and Shiro (shiro:) taglibraries in the page.

Step 4b: Add Shiro Guest and User tags

The home.jsp file was further modified in the page body (right after the <h1> welcome message) to include both the <shiro:guest> and<shiro:user> tags:

<p>Hi <shiro:guest>Guest</shiro:guest><shiro:user>
<%
    //This should never be done in a normal page and should exist in a proper MVC controller of some sort, but for this
    //tutorial, we'll just pull out Stormpath Account data from Shiro's PrincipalCollection to reference in the
    //<c:out/> tag next:

    request.setAttribute("account", org.apache.shiro.SecurityUtils.getSubject().getPrincipals().oneByType(java.util.Map.class));

%>
<c:out value="${account.givenName}"/></shiro:user>!
    ( <shiro:user><a href="<c:url value="/logout"/>">Log out</a></shiro:user>
    <shiro:guest><a href="<c:url value="/login.jsp"/>">Log in</a></shiro:guest> )
</p>

It’s a little hard to read given the formatting, but two tags are used here:

  • <shiro:guest>: This tag will only display its internal contents if the current Shiro Subject is an application ‘guest’. Shiro defines a guestas any Subject that has not logged in to the application, or is not remembered from a previous login (using Shiro’s ‘remember me’ functionality).

  • <shiro:user>: This tag will only display its internal contents if the current Shiro Subject is an application ‘user’. Shiro defines a user as any Subject that is currently logged in to (authenticated with) the application or one that is remembered from a previous login (using Shiro’s ‘remember me’ functionality).

The above code snippet will render the following if the Subject is a guest:

Hi Guest! (Log in)

where ‘Log in’ is a hyperlink to /login.jsp

It will render the following if the Subject is a ‘user’:

Hi jsmith! (Log out)

Assuming ‘jsmith’ is the username of the account logged in. ‘Log out’ is a hyperlink to the ‘/logout’ url handled by the Shiro logout filter.

As you can see, you can turn off or on entire page sections, features and UI components. In addition to <shiro:guest> and <shiro:user>, Shiro supports many other useful JSP tags that you can use to customize the UI based on various things known about the current Subject.

Step 4c: Run the webapp

After checking out the step4 branch, go ahead and run the web app:

$ mvn jetty:run

Try visiting localhost:8080 as a guest, and then login. After successful login, you will see the page content change to reflect that you’re now a known user!

Hit ctl-C (or cmd-C on a mac) to shut down the web app.

Step 5: Allow Access to Only Authenticated Users

While you can change page content based on Subject state, often times you will want to restrict entire sections of your webapp based on if someone has proven their identity (authenticated) during their current interaction with the web application.

This is especially important if a user-only section of a webapp shows sensitive information, like billing details or the ability to control other users.

Perform the following git checkout command to load the step5 branch:

git checkout step5

Step 5 introduces the following 3 changes:

  1. We added a new section (url path) of the webapp that we want to restrict to only authenticated users.

  2. We changed shiro.ini to tell shiro to only allow authenticated users to that part of the web app.

  3. We modified the home page to change its output based on if the current Subject is authenticated or not

Step 5a: Add a new restricted section

A new src/main/webapp/account directory was added. This directory (and all paths below it) simulates a ‘private’ or ‘authenticated only’ section of a website that you might want to restrict to only logged in users. The src/main/webapp/account/index.jsp file is just a placeholder for a simulated ‘home account’ page.

Step 5b: Configure shiro.ini

shiro.ini was modified by adding the following line at the end of the [urls] section:

/account/** = authc

This Shiro filter chain definition means “Any requests to /account (or any of its sub-paths) must be authenticated”.

But what happens if someone tries to access that path or any of its children paths?

But do you remember in Step 3 when we added the following line to the [main] section:

shiro.loginUrl = /login.jsp

This line automatically configured the authc filter with our webapp’s login URL.

Based on this line of config, the authc filter is now smart enough to know that if the current Subject is not authenticated when accessing/account, it will automatically redirect the Subject to the /login.jsp page. After successful login, it will then automatically redirect the user back to the page they were trying to access (/account). Convenient!

Step 5c: Update our home page

The final change for Step 5 is to update the /home.jsp page to let the user know they can access the new part of the web site. These lines were added below the welcome message:

<shiro:authenticated><p>Visit your <a href="<c:url value="/account"/>">account page</a>.</p></shiro:authenticated>
<shiro:notAuthenticated><p>If you want to access the authenticated-only <a href="<c:url value="/account"/>">account page</a>,
    you will need to log-in first.</p></shiro:notAuthenticated>

The <shiro:authenticated> tag will only display the contents if the current Subject has already logged in (authenticated) during their current session. This is how the Subject knows they can go visit a new part of the website.

The <shiro:notAuthenticated> tag will only display the contents if the current Subject is not yet authenticated during their current session.

But did you notice that the notAuthenticated content still has a URL to the /account section? That’s ok - our authc filter will handle the login-and-then-redirect flow as described above.

Fire up the webapp with the new changes and try it out!

Step 5d: Run the webapp

After checking out the step5 branch, go ahead and run the web app:

$ mvn jetty:run

Try visiting localhost:8080. Once there, click the new /account link and watch it redirect you to force you to log in. Once logged in, return to the home page and see the content change again now that you’re authenticated. You can visit the account page and the home page as often as you want, until you log out. Nice!

Hit ctl-C (or cmd-C on a mac) to shut down the web app.

Step 6: Role-Based Access Control

In addition to controlling access based on authentication, it is often a requirement to restrict access to certain parts of the application based on what role(s) are assigned to the current Subject.

Perform the following git checkout command to load the step5 branch:

git checkout step6

Step 6a: Add Roles

In order to perform Role-Based Access Control, we need Roles to exist.

The fastest way to do that in this tutorial is to populate some Groups within Stormpath (in Stormpath, a Stormpath Group can serve the same purpose of a Role).

To do this, log in to the UI and navigate as follows:

Directories > Apache Shiro Tutorial Webapp Directory > Groups

Add the following three groups:

  • Captains

  • Officers

  • Enlisted

(to keep with our Star-Trek account theme :) )

Once you’ve created the groups, add the Jean-Luc Picard account to the Captains and Officers groups. You might want to create some ad-hoc accounts and add them to whatever groups you like. Make sure some of the accounts don’t overlap groups so you can see changes based on separate Groups assigned to user accounts.

Step 6b: RBAC Tags

We update the /home.jsp page to let the user know what roles they have and which ones they don’t. These messages are added in a new<h2>Roles</h2> section of the home page:

<h2>Roles</h2>

<p>Here are the roles you have and don't have. Log out and log back in under different user
    accounts to see different roles.</p>

<h3>Roles you have:</h3>

<p>
    <shiro:hasRole name="Captains">Captains<br/></shiro:hasRole>
    <shiro:hasRole name="Officers">Bad Guys<br/></shiro:hasRole>
    <shiro:hasRole name="Enlisted">Enlisted<br/></shiro:hasRole>
</p>

<h3>Roles you DON'T have:</h3>

<p>
    <shiro:lacksRole name="Captains">Captains<br/></shiro:lacksRole>
    <shiro:lacksRole name="Officers">Officers<br/></shiro:lacksRole>
    <shiro:lacksRole name="Enlisted">Enlisted<br/></shiro:lacksRole>
</p>

The <shiro:hasRole> tag will only display the contents if the current Subject has been assigned the specified role.

The <shiro:lacksRole> tag will only display the contents if the current Subject has not been assigned the specified role.

Step 6c: RBAC filter chains

An exercise left to the reader (not a defined step) is to create a new section of the website and restrict URL access to that section of the website based on what role is assigned to the current user.

Hint: Create a filter chain definition for that new part of the webapp using the roles filter

Step 6d: Run the webapp

After checking out the step6 branch, go ahead and run the web app:

$ mvn jetty:run

Try visiting localhost:8080 and log in with different user accounts that are assigned different roles and watch the home page’s Roles section content change!

Hit ctl-C (or cmd-C on a mac) to shut down the web app.

Step 7: Permission-Based Access Control

Role-based access control is good for many use cases, but it suffers from one major problem: you can’t add or delete roles at runtime. Role checks are hard-coded with role names, so if you changed the role names or role configuration, or add or remove roles, you have to go back and change your code!

Because of this, Shiro has a powerful marquis feature: built-in support for permissions. In Shiro, a permission is a raw statement of functionality, for example ‘open a door’ ‘create a blog entry’, ‘delete the jsmith user’, etc. Permissions reflect your application’s raw functionality, so you only need to change permission checks when you change your application’s functionlity - not if you want to change your role or user model.

To demonstrate this, we will create some permissions and assign them to a user, and then customize our web UI based on a user’s authorization (permissions).

Step 7a: Add Permissions

Shiro Realms are read-only components: every data store models roles, groups, permissions, accounts, and their relationships differently, so Shiro doesn’t have a ‘write’ API to modify these resources. To modify the underlying the model objects, you just modify them directly via whatever API you desire. Your Shiro Realm then knows how to read this information and represent it in a format Shiro understands.

As such, since we’re using Stormpath in this sample app, we’ll assign permissions to an account and group in a Stormpath API-specific way.

Let’s execute a cURL request to add some permissions to our previously created Jean-Luc Picard account. Using that account’s href URL, we’ll post some apacheShiroPermissions to the account via custom data:

153244_L3ng_1417419.png

where $JLPICARD_ACCOUNT_ID matches the uid of the Jean-Luc Picard you created at the beginning of this tutorial.

This adds two permissions directly to the Stormpath Account:

  • ship:NCC-1701-D:command

  • user:jlpicard:edit

These use Shiro’s WildcardPermission syntax.

The first basically means the ability to ‘command’ the ‘ship’ with identifier ‘NCC-1701-D’. This is an example of an instance-levelpermission: controlling access to a specific instance NCC-1701-D of a resource ship

The second is also an instance-level permission that states the ability to edit the user with identifier jlpicard.

How permissions are stored in Stormpath, as well as how to customize storage and access options in Stormpath is out of scope for this document, but this is explained in the Shiro Stormpath plugin documentation.

Step 7b: Permission Tags

Just as we have JSP tags for role checks, parallel tags exist for permission checks as well. We update the /home.jsp page to let the user know if they’re allowed to do something or not based on the permissions that are assigned to them. These messages are added in a new<h2>Permissions</h2> section of the home page:

<h2>Permissions</h2>

<ul>
    <li>You may <shiro:lacksPermission name="ship:NCC-1701-D:command"><b>NOT</b> </shiro:lacksPermission> command the <code>NCC-1701-D</code> Starship!</li>
    <li>You may <shiro:lacksPermission name="user:${account.username}:edit"><b>NOT</b> </shiro:lacksPermission> edit the ${account.username} user!</li>
</ul>

When you visit the home page the first time, before you log in, you will see the following output:

You may NOT command the NCC-1701-D Starship!
You may NOT edit the user!

But after you log in with your Jean-Luc Picard account, you will see this instead:

You may command the NCC-1701-D Starship!
You may edit the user!

You can see that Shiro resolved that the authenticated user had permissions, and the output was rendered in an appropriate way.

You can also use the <shiro:hasPermission> tag for affirmative permission checks.

Finally, we’ll call to attention an extremely powerful feature with permission checks. Did you see how the second permission check used aruntime generated permission value?

<shiro:lacksPermission name="user:${account.username}:edit"> ...

The ${account.username} value is interpreted at runtime and forms the final user:aUsername:edit value, and then the final String value is used for the permission check.

This is extremely powerful: you can perform permission checks based on who the current user is and what is currently being interacted with. These runtime-based instance-level permission checks are a foundational technique for developing highly customizable and secure applications.

Step 7c: Run the webapp

After checking out the step7 branch, go ahead and run the web app:

$ mvn jetty:run

Try visiting localhost:8080 and log in and out of the UI with your Jean-Luc Picard account (and other accounts), and see the page output change based on what permissions are assigned (or not)!

Hit ctl-C (or cmd-C on a mac) to shut down the web app.

Summary

We hope you have found this introductory tutorial for Shiro-enabled webapps useful. In coming editions of this tutorial, we will cover:

  • Plugging in different user data stores, like an RDBMS or NoSQL data store.

Fixes and Pull Requests

Please send any fixes for errata as a GitHub Pull Request to the https://github.com/lhazlewood/apache-shiro-tutorial-webapprepository. We appreciate it!!!

转载于:https://my.oschina.net/heroShane/blog/198262


http://www.niftyadmin.cn/n/1115699.html

相关文章

Web.简单配置

XML 元素不仅是大小写敏感的&#xff0c;而且它们还对出现在其他元素中的次序敏感。所有这些元素都是可选的。因此&#xff0c;可以省略掉某一元素&#xff0c;但不能把它放于不正确的位置。 icon icon元素指出IDE和GUI工具用来表示Web应用的一个和两个图像文件的位置。 displa…

vue中的具名插槽

<div id"app"><!-- 1.2 想要指定插入指定位置&#xff0c;就得指定一下插入到slot的名字&#xff0c;通过slot属性来指定, 这个slot的值必须和下面slot组件得name值对应上 --><index><h1 slot"header">我爱学习前端</h1><…

在Android 系统中增加C 可执行程序来访问硬件驱动程序

在Android 系统中增加C 可执行程序来访问硬件驱动程序。 在前一篇文章http://www.linuxidc.com/Linux/2011-07/38977中&#xff0c;我们介绍了如何在Ubuntu 上为Android 系统编写Linux 内核驱动程序。在这个名为hello的Linux 内核驱动程序中&#xff0c;创建三个不同的文件节…

mysql 新用户添加和权限

1进入数据库首先,启动数据库服务&#xff0c; sudo service mysql start2. 添加密码  因为MySQL的root用户默认是没有密码&#xff0c;所以直接连接。但作为最该权限用户&#xff0c;没有秘密是绝对不安全&#xff0c;不能是不是DBA都应该给root用户添加一个密码为root 添加密…

oracle O7_DICTIONARY_ACCESSIBILITY 参数

O7_DICTIONARY_ACCESSIBILITY是用来控制select any table权限是否可以访问data dictionary的&#xff0c;主要用来保护数据字典。oracle建议把O7_DICTIONARY_ACCESSIBILITY参数设为 false&#xff0c;9i及以上版本默认为false&#xff0c;8i及以前版本默认为true。如果该参数为…

带有×的EditText

代码&#xff1a; EditTextWithDel.java&#xff08;直接复制&#xff09;&#xff1a; 1 package com.sunday.customs;2 3 4 import com.example.customs.R;5 6 import android.content.Context;7 import android.graphics.Rect;8 import android.graphics.drawable.Drawable;…

在Ubuntu 上为Android 增加硬件抽象层(HAL)模块访问Linux 内核驱动程序

在Ubuntu 上为Android 增加硬件抽象层&#xff08;HAL&#xff09;模块访问Linux 内核驱动程序 在Android 硬件抽象层&#xff08;HAL&#xff09;概要介绍和学习计划一文中&#xff0c;我们简要介绍了在Android 系统为为硬件编写驱动程序的方法。 简单来说&#xff0c;硬件驱…

支持向量机SVM、优化问题、核函数

1、介绍 它是一种二类分类模型&#xff0c;其基本模型定义为特征空间上的间隔最大的线性分类器&#xff0c;即支持向量机的学习策略便是间隔最大化&#xff0c;最终可转化为一个凸二次规划问题的求解。 2、求解过程 1、数据分类—SVM引入 假设在一个二维平面中有若干数据点(x,y…