In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-16 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >
Share
Shulou(Shulou.com)06/01 Report--
This article mainly explains "how to configure Shiro in the configuration file of Spring". The explanation in this article is simple and clear and easy to learn and understand. Please follow the editor's train of thought to study and learn "how to configure Shiro in the configuration file of Spring".
First integrate Spring, SpringMVC and Shiro
Org.springframework
Spring-context
4.3.18.RELEASE
Org.springframework
Spring-webmvc
4.3.18.RELEASE
Org.apache.shiro
Shiro-all
1.3.2
Net.sf.ehcache
Ehcache-core
2.6.2
Configure the filter for Shiro in the web.xml file
ShiroFilter
Org.springframework.web.filter.DelegatingFilterProxy
TargetFilterLifecycle
True
ShiroFilter
/ *
Create a configuration file (ehcache-shiro.xml) for Shiro
Configure Shiro in the configuration file of Spring
/ login.jsp = anon
# everything else requires authentication:
/ * * = authc
Once the configuration is complete, start the project.
Work flow
Insert a picture description here
Shiro intercepts all requests through the ShiroFilter configured in the web.xml configuration file and specifies which pages are protected and their permissions by configuring filterChainDefinitions.
URL permission configuration
The configuration of the [urls] section, which is in the format of url= interceptor [parameters]. If the url of the current request matches a url pattern in the [urls] part (url pattern uses Ant style matching), its configured interceptor will be executed, where:
Anon: this interceptor indicates anonymous access, that is, it can be accessed without login
Authc: this interceptor indicates that authentication is required before it can be accessed
Logout: log out
Roles: role filter
Example:
/ login.jsp = anon
# everything else requires authentication:
/ * * = authc
It should be noted that the url permission takes the first match first, that is, the interceptor chain corresponding to the first matching url pattern is used from scratch, such as:
/ bb/**=filter1
/ bb/aa=filter2
/ * * = filter3
If the url of the request is / bb/aa, because the match is in the order of declaration, then filter1 will be used for interception.
Shiro authentication process
Get the current Subject-- SecurityUtils.getSubject ()
Verify whether the current user has been authenticated-- call the isAuthenticated () method of Subject
If it is not authenticated, the user name and password are encapsulated as UsernamePasswordToken objects
Perform login-transfer the login (UsernamePasswordToken) method of Subject
Customize the method of Realm, get the corresponding record from the database, and return it to Shiro
Custom classes inherit from org.apache.shiro.realm.AuthenticatingRealm
Implement the doGetAuthenticationInfo (AuthenticationToken) method
Shiro completes the comparison of user name and password
To implement this, first create a login.jsp:
Title
Login Page
Username:
Password:
Then write the controller:
Package com.wwj.shiro.handlers
Import org.apache.shiro.SecurityUtils
Import org.apache.shiro.authc.AuthenticationException
Import org.apache.shiro.authc.UsernamePasswordToken
Import org.apache.shiro.subject.Subject
Import org.springframework.stereotype.Controller
Import org.springframework.web.bind.annotation.RequestMapping
Import org.springframework.web.bind.annotation.RequestParam
@ Controller
Public class ShiroHandler {
@ RequestMapping ("/ shiroLogin")
Public String login (@ RequestParam ("username") String username, @ RequestParam ("password") String password) {
/ / get the current Subject
Subject currentUser = SecurityUtils.getSubject ()
/ / verify whether the current user has been authenticated
If (! currentUser.isAuthenticated ()) {
/ / encapsulate the user name and password as UsernamePasswordToken objects
UsernamePasswordToken token = new UsernamePasswordToken (username,password)
Token.setRememberMe (true)
Try {
/ / perform login
CurrentUser.login (token)
} catch (AuthenticationException ae) {
System.out.println ("login failed" + ae.getMessage ())
}
}
Return "redirect:/list.jsp"
}
}
Write a custom Realm:
Package com.wwj.shiro.realms
Import org.apache.shiro.authc.*
Import org.apache.shiro.realm.AuthenticatingRealm
Public class ShiroRealm extends AuthenticatingRealm {
/ * *
* @ param authenticationToken this parameter is actually the parameter token passed in by the login () method after encapsulating the user name and password in the controller method
* @ return
* @ throws AuthenticationException
, /
@ Override
Protected AuthenticationInfo doGetAuthenticationInfo (AuthenticationToken authenticationToken) throws AuthenticationException {
/ / transfer the parameters back to UsernamePasswordToken
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken
/ / withdraw the user name from UsernamePasswordToken
String username = token.getUsername ()
/ / call the database method to query the records corresponding to username from the data table
System.out.println ("get Username from database:" + username + "corresponding user information")
/ / if the user does not exist, you can throw an exception
If ("unknow" .equals (username)) {
Throw new UnknownAccountException ("user does not exist!")
}
/ / decide whether other exceptions need to be thrown according to the user's information
If ("monster" .equals (username)) {
Throw new LockedAccountException ("user is locked!")
}
/ * build an AuthenticationInfo object and return it according to the user's information. The commonly used implementation class is SimpleAuthenticationInfo.
* the following information is obtained from the database:
* principal: authenticated entity information, which can be username or user entity class object corresponding to the data table
* credentials: password
* realmName: the name of the current realm object. Call the getName () method of the parent class.
, /
Object principal = username
Object credentials = "123456"
String realmName = getName ()
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo (principal,credentials,realmName)
Return info
}
}
Remember to intercept form requests in the Spring configuration file:
/ login.jsp = anon
/ shiroLogin = anon
/ logout = logout
# everything else requires authentication:
/ * * = authc
After successful login, jump to list.jsp:
Title
List Page
Logout
Here, a logout request is implemented because Shiro will have a cache after a successful login, and no matter whether the user name is valid or not, the login will be successful, so a logout operation is performed here.
After the preparation is completed, you can finally start the project.
Insert a picture description here
If you do not log in, you will not be able to access other pages. If you enter the wrong user name, you will not be able to log in successfully and you will not be able to access other pages:
Insert a picture description here
If you enter the correct user name and password, the login is successful and you can visit other pages:
Insert a picture description here
Let's review the above certification process:
First of all, there is a form in the login.jsp page for login. When the user enters the user name and password and clicks to log in, the request will be blocked by the ShiroHandler controller.
Verify whether the user has been authenticated in ShiroHandler, if not, encapsulate the user name and password into a UsernamePasswordToken object, and perform login
When the login is performed, the UsernamePasswordToken object is passed into the input parameter of the doGetAuthenticationInfo () method of the ShiroRealm class, where the data is further verified.
The process of password verification
In the example just now, we have achieved the control of page permissions before and after the user logs in. In fact, we do not write the code for password comparison in the program, and the login logic obviously verifies the password, so we can guess that Shiro must have helped us to complete the password verification.
Let's make a breakpoint in the getPassword () method in the UserNamePasswordToken class:
Insert a picture description here
At this point, start the project as debug, enter the user name and password in the form, click login, and the program can be suspended at this method:
Insert a picture description here
We went forward to find out where the logic of password verification was performed and found that in the doCredentialsMatch () method:
Insert a picture description here
Then look at the parameters on the right:
Insert a picture description here
Isn't that the password I entered in the form and the password I queried in the datasheet? This confirms that Shiro helps us verify the password here.
If you look ahead, you can find:
Insert a picture description here
Shiro actually verifies passwords with CredentialsMatcher, so why bother to find CredentialsMatcher?
CredentialsMatcher is an interface. Let's take a look at its implementation class:
Insert a picture description here
So I believe you already know what to do next, yes, password encryption, and encryption is done through CredentialsMatcher.
MD5 encryption
There are actually many encryption algorithms. Here, take md5 encryption as an example.
Modify the configuration of the custom Realm in the Spring configuration file:
Here, because the Md5CredentialsMatcher class has expired, Shiro recommends using HashedCredentialsMatcher directly.
After this configuration, the password entered from the form can be MD5 encrypted automatically, but the password obtained from the data table is still in clear text, so you also need to encrypt the password with MD5:
Public static void main (String [] args) {
String algorithmName = "MD5"
Object credentials = "123456"
Object salt = null
Int hashIterations = 5
Object result = new SimpleHash (algorithmName, credentials, salt, hashIterations)
System.out.println (result)
}
This code can refer to the underlying implementation of Shiro. We encrypt it in the same way as Shiro, and both passwords are encrypted. Run the project with debug and find the place where Shiro verifies the password again:
Insert a picture description here
The password I entered in the form is 123456. After verification, it is found that the ciphertext of the two passwords are the same, so the login is successful.
Consider the case of duplicate passwords
Just now, the password was encrypted, which further solved the security problem of the password, but there is a new problem in front of us. If the passwords of two users are the same, even if the passwords are encrypted, because the ciphertext is the same, there will still be security problems, so can it be realized that even if the passwords are the same, the generated ciphertext can be different?
Yes, of course. Here we need to use a credentialsSalt attribute (here we assume that the ciphertext is re-encrypted using the user name as the identity):
Public static void main (String [] args) {
String algorithmName = "MD5"
Object credentials = "123456"
Object salt = ByteSource.Util.bytes ("aaa")
/ / Object salt = ByteSource.Util.bytes ("bbb")
Int hashIterations = 5
Object result = new SimpleHash (algorithmName, credentials, salt, hashIterations)
System.out.println (result)
}
In this way, we generate two different ciphertexts, even if the passwords are the same:
C8b8a6de6e890dea8001712c9e149496
3d12ecfbb349ddbe824730eb5e45deca
Now that the encryption has been modified here, we will also change it when the form password is encrypted:
@ Override
Protected AuthenticationInfo doGetAuthenticationInfo (AuthenticationToken authenticationToken) throws AuthenticationException {
/ / transfer the parameters back to UsernamePasswordToken
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken
/ / withdraw the user name from UsernamePasswordToken
String username = token.getUsername ()
/ / call the database method to query the records corresponding to username from the data table
System.out.println ("get Username from database:" + username + "corresponding user information")
/ / if the user does not exist, you can throw an exception
If ("unknow" .equals (username)) {
Throw new UnknownAccountException ("user does not exist!")
}
/ / decide whether other exceptions need to be thrown according to the user's information
If ("monster" .equals (username)) {
Throw new LockedAccountException ("user is locked!")
}
/ * build an AuthenticationInfo object and return it according to the user's information. The commonly used implementation class is SimpleAuthenticationInfo.
* the following information is obtained from the database:
* principal: authenticated entity information, which can be username or user entity class object corresponding to the data table
* credentials: password
* realmName: the name of the current realm object. Call the getName () method of the parent class.
, /
Object principal = username
Object credentials = null
/ / A pair of user names are judged
If ("aaa" .equals (username)) {
Credentials = "c8b8a6de6e890dea8001712c9e149496"
} else if ("bbb" .equals (username)) {
Credentials = "3d12ecfbb349ddbe824730eb5e45deca"
}
String realmName = getName ()
ByteSource credentialsSalt = ByteSource.Util.bytes (username)
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo (principal,credentials,credentialsSalt,realmName)
Return info
}
In this way, the security problem of duplicate passwords is easily solved.
Multi-Relam configuration
What you just implemented is the case of a single Relam. Let's take a look at the configuration between multiple Relam.
First customize the second Relam:
Package com.wwj.shiro.realms
Import org.apache.shiro.authc.*
Import org.apache.shiro.crypto.hash.SimpleHash
Import org.apache.shiro.realm.AuthenticatingRealm
Import org.apache.shiro.util.ByteSource
Public class ShiroRealm2 extends AuthenticatingRealm {
@ Override
Protected AuthenticationInfo doGetAuthenticationInfo (AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println ("ShiroRealm2...")
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken
String username = token.getUsername ()
System.out.println ("get Username from database:" + username + "corresponding user information")
If ("unknow" .equals (username)) {
Throw new UnknownAccountException ("user does not exist!")
}
If ("monster" .equals (username)) {
Throw new LockedAccountException ("user is locked!")
}
Object principal = username
Object credentials = null
If ("aaa" .equals (username)) {
Credentials = "ba89744a3717743bef169b120c052364621e6135"
} else if ("bbb" .equals (username)) {
Credentials = "29aa55fcb266eac35a6b9c1bd5eb30e41d4bfd8d"
}
String realmName = getName ()
ByteSource credentialsSalt = ByteSource.Util.bytes (username)
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo (principal,credentials,credentialsSalt,realmName)
Return info
}
Public static void main (String [] args) {
String algorithmName = "SHA1"
Object credentials = "123456"
Object salt = ByteSource.Util.bytes ("bbb")
Int hashIterations = 5
Object result = new SimpleHash (algorithmName, credentials, salt, hashIterations)
System.out.println (result)
}
}
Here you simply copy the code for the first Relam and change the encryption to SHA1.
Next, modify the configuration file of Spring:
/ login.jsp = anon
/ shiroLogin = anon
/ logout = logout
# everything else requires authentication:
/ * * = authc
The comments are the ones that need to be changed.
At this point, we start the project to log in to view the console information:
Insert a picture description here
You can see that both Relam are called.
Authentication strategy
Since there are multiple Relam, there must be differences in authentication policies. For example, whether a successful authentication is a success in multiple Relam or a successful authentication in all Relam is considered a success. Shiro provides three strategies for this:
FirstSuccessfulStrategy: as long as one Relam authentication is successful, only the authentication information of the first Relam authentication is returned, and the rest is ignored.
AtLeastOneSuccessfulStrategy: as long as one Relam authentication is successful, unlike FirstSuccessfulStrategy, it will return the authentication information of all Relam authentication successes
AllSuccessfulStrategy: all Relam authentication is considered successful, and all Relam authentication information is returned.
The default strategy is AtLeastOneSuccessfulStrategy, which you can experience by looking at the source code.
To modify the default authentication policy, you can modify the configuration file of Spring:
Authorization
Authorization is also called access control, that is, to control who accesses which resources in the application. You need to understand the following key objects in authorization:
Principal: users who access the application
Resources: url that users can access in the application
Permissions: atomic authorization units in security policy
Roles: collection of permissions
Let's implement a case to feel the role of authorization, create new aaa.jsp and bbb.jsp files, and modify list.jsp:
Title
List Page
Aaa Page
Bbb Page
Logout
The situation now is that after a successful login, you will be able to access the aaa and bbb pages:
Insert a picture description here
But I want to achieve the effect that only the current user can access the specified page. For example, if I log in as an aaa user, I will only be able to access aaa.jsp but not bbb.jsp;. Similarly, if I log in as a bbb user, I can only access bbb.jsp but not aaa.jsp. How can I achieve this?
The implementation is actually very simple, by modifying the configuration file of Sping:
/ login.jsp = anon
/ shiroLogin = anon
/ logout = logout
/ aaa.jsp = roles [aaa]
/ bbb.jsp = roles [bbb]
# everything else requires authentication:
/ * * = authc
Start the project to see how it works:
Insert a picture description here
Here is a pit where you need to put a reference to Relam in the securityManager before writing the authorization:
Otherwise, the program will not work properly.
Now, although the permission has been added, whether you are an aaa user or a bbb user, you cannot access the page. Shiro automatically jumps to the unauthorized page. We also need to do some actions to modify the ShiroRelam class:
Package com.wwj.shiro.realms
Import org.apache.shiro.authc.*
Import org.apache.shiro.authz.AuthorizationInfo
Import org.apache.shiro.authz.SimpleAuthorizationInfo
Import org.apache.shiro.crypto.hash.SimpleHash
Import org.apache.shiro.realm.AuthorizingRealm
Import org.apache.shiro.subject.PrincipalCollection
Import org.apache.shiro.util.ByteSource
Import java.util.HashSet
Import java.util.Set
Public class ShiroRealm extends AuthorizingRealm {
@ Override
Protected AuthenticationInfo doGetAuthenticationInfo (AuthenticationToken authenticationToken) throws AuthenticationException {
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken
String username = token.getUsername ()
System.out.println ("get Username from database:" + username + "corresponding user information")
If ("unknow" .equals (username)) {
Throw new UnknownAccountException ("user does not exist!")
}
If ("monster" .equals (username)) {
Throw new LockedAccountException ("user is locked!")
}
Object principal = username
Object credentials = null
If ("aaa" .equals (username)) {
Credentials = "c8b8a6de6e890dea8001712c9e149496"
} else if ("bbb" .equals (username)) {
Credentials = "3d12ecfbb349ddbe824730eb5e45deca"
}
String realmName = getName ()
ByteSource credentialsSalt = ByteSource.Util.bytes (username)
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo (principal,credentials,credentialsSalt,realmName)
Return info
}
/ * *
* method that will be called back by Shiro when authorizing
* @ param principalCollection
* @ return
, /
@ Override
Protected AuthorizationInfo doGetAuthorizationInfo (PrincipalCollection principalCollection) {
/ / get the information of the logged in user
Object principal = principalCollection.getPrimaryPrincipal ()
/ / get the role of the current user
Set roles = new HashSet ()
Roles.add ("aaa")
If ("bbb" .equals (principal)) {
Roles.add ("bbb")
}
/ / create SimpleAuthorizationInfo and set its roles property
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo (roles)
Return info
}
}
First of all, the inherited class is modified to inherit the AuthorizingRealm class. Authentication can be completed by implementing the doGetAuthenticationInfo () method of this class, and authorization can be completed through the doGetAuthorizationInfo () method, so the source code does not need to be moved, just add the following doGetAuthorizationInfo () method to see the running effect:
Insert a picture description here
You can see that aaa users can only access aaa.jsp but not bbb.jsp, but bbb users can access two pages, and if you take a closer look at the method you just added, you can see why.
@ Override
Protected AuthorizationInfo doGetAuthorizationInfo (PrincipalCollection principalCollection) {
/ / get the information of the logged in user
Object principal = principalCollection.getPrimaryPrincipal ()
/ / get the role of the current user
Set roles = new HashSet ()
Roles.add ("aaa")
If ("bbb" .equals (principal)) {
Roles.add ("bbb")
}
/ / create SimpleAuthorizationInfo and set its roles property
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo (roles)
Return info
}
Because no matter what user logs in, I add aaa users to roles, so bbb users have aaa user rights, the permissions are completely controlled by you, and you can write whatever you want.
Annotations to achieve authorization
Let's take a look at a few notes on authorization:
@ RequiresAuthentication: indicates that the current Subject has been authenticated through login; that is, Subject. IsAuthenticated () returns true
@ RequiresUser: indicates that the current Subject has been authenticated or by remembering the
RequiresGuest: indicates that the current Subject does not have authentication or by remembering that I have logged in, that is, the identity of a tourist.
@ RequiresRoles (value= {"aaa", "bbb"}, logical=Logical.AND): indicates that the current Subject requires roles aaa and bbb
@ RequiresPermissions (value= {"user:a", "user:b"}, logical= Logical.OR): indicates that the current Subject requires permission user:an or user:b
Delete the role filter in the Spring configuration file and define a Service:
Package com.wwj.shiro.service
Import org.apache.shiro.authz.annotation.RequiresRoles
Import org.springframework.stereotype.Service
@ Service
Public class ShiroService {
@ RequiresRoles ({"aaa"})
Public void test () {
System.out.println ("test...")
}
}
Add the annotation @ RequiresRoles ({"aaa"}) to the test () method, meaning that the method is accessible only to aaa users, and then add a method to ShiroHandler:
@ Autowired
Private ShiroService shiroService
@ RequestMapping ("/ testAnnotation")
Public String testAnnotation () {
ShiroService.test ()
Return "redirect:/list.jsp"
}
At this point, when you access the testAnnotation request, only the aaa user can successfully access it, and the bbb user will throw an exception.
UiresRoles (value= {"aaa", "bbb"}, logical=Logical.AND): indicates that the current Subject requires roles aaa and bbb
@ RequiresPermissions (value= {"user:a", "user:b"}, logical= Logical.OR): indicates that the current Subject requires permission user:an or user:b
Delete the role filter in the Spring configuration file and define a Service:
Package com.wwj.shiro.service
Import org.apache.shiro.authz.annotation.RequiresRoles
Import org.springframework.stereotype.Service
@ Service
Public class ShiroService {
@ RequiresRoles ({"aaa"})
Public void test () {
System.out.println ("test...")
}
}
Add the annotation @ RequiresRoles ({"aaa"}) to the test () method, meaning that the method is accessible only to aaa users, and then add a method to ShiroHandler:
@ Autowired
Private ShiroService shiroService
@ RequestMapping ("/ testAnnotation")
Public String testAnnotation () {
ShiroService.test ()
Return "redirect:/list.jsp"
}
At this point, when you access the testAnnotation request, only the aaa user can successfully access it, and the bbb user will throw an exception.
Thank you for reading, the above is the content of "how to configure Shiro in the configuration file of Spring". After the study of this article, I believe you have a deeper understanding of how to configure Shiro in the configuration file of Spring, and the specific use needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.