This is the fourth day of my participation in the August More text Challenge. For details, see:August is more challenging
Introduction to Shiro
Apache Shiro is a security framework for Java designed to simplify authentication and authorization. Shiro can be used in both JavaSE and JavaEE projects. It is mainly used to handle identity authentication, authorization, enterprise session management and encryption. Shiro’s specific function points are as follows:
(1) Identity authentication/login to verify whether the user has the appropriate identity;
(2) Authorization, that is, permission authentication, to verify whether an authenticated user has a certain permission; That is, it determines whether a user can do something. For example, it verifies whether a user has a role. Or fine-grained verification of whether a user has a certain permission on a certain resource;
(3) Session management, that is, after the user login is a session, before the exit, all its information in the session; The session can be in a normal JavaSE environment or in a Web environment.
(4) Encryption, to protect the security of data, such as password encryption stored in the database, rather than plain text storage;
(5) Web support, which can be easily integrated into the Web environment;
Caching: indicates the cache. For example, after a user logs in, the user information and roles and permissions do not need to be checked every time. This improves efficiency.
(6) Shiro supports concurrent verification of multi-threaded applications. If you start another thread in one thread, permissions can be automatically propagated.
(7) Provide test support;
(8) Allow a user to pretend to be another user if they allow access;
(9) Remember me, this is a very common function, that once logged in, the next time to do not log in.
Shiro authentication, authorization, custom Realm
1. Shiro certification
Let’s pass the code test
Add dependencies to pom.xml
</groupId> </groupId> </artifactId> </artifactId> </artifactId> </version> 4.12</version> </dependency> <dependency> < groupId > org, apache shiro < / groupId > < artifactId > shiro - core < / artifactId > < version > 1.2.3 < / version > < / dependency >Copy the code
Following the certification process shown in the figure above, we can write a test class
Public class ShiroTest {// Create a realm SimpleAccountRealm SimpleAccountRealm = new SimpleAccountRealm(); Add an account / * * * * / @ Before public void addUser () {simpleAccountRealm. AddAccount (" CodeTiger ", "6666", "admin"); } @test /** * Test shiro authentication */ public void testAuthentication() {// Create SecurityManager to authenticate DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager(); defaultSecurityManager.setRealm(simpleAccountRealm); / / subject to submit certification request the SecurityUtils setSecurityManager (defaultSecurityManager); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken("CodeTiger", "6666"); subject.login(token); System.out.println(" Authenticated: "+ subject.isauthenticated ()); subject.logout(); System.out.println(" Authenticated: "+ subject.isauthenticated ()); }}Copy the code
Run the test method, output
true
false
Copy the code
2. Shiro authorization
Add a method to the above class to test
@test /** * Test Shiro's authorization process */ public void testAuthorizer() {// Create securityManager DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager(); defaultSecurityManager.setRealm(simpleAccountRealm); SecurityUtils.setSecurityManager(defaultSecurityManager); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken("CodeTiger", "6666"); subject.login(token); subject.checkRole("admin"); subject.checkRoles("admin","user"); }Copy the code
3, Realm
Shiro has a number of built-in realms. We’ll talk about IniRealm and JdbcRealm.
(1), IniRealm
IniRealm authenticates and authorizes by configuring role information in files.
Create a new test class
Public class IniRealmTest {// specify the path to the file, IniRealm IniRealm = new IniRealm("classpath:user.ini"); @Test public void testIniRealm() { DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager(); defaultSecurityManager.setRealm(iniRealm); SecurityUtils.setSecurityManager(defaultSecurityManager); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken("CodeTiger", "6666"); subject.login(token); System.out.println(subject.isAuthenticated()); Subject.checkrole ("admin"); Subject.checkpermission ("user:delete"); }}Copy the code
As you can see, we created an IniRealm instance by reading a file, and then we went to the classpath to create user.ini
[users]
CodeTiger=6666,admin
[roles]
admin=user:delete,user:update
Copy the code
Run the test case to pass.
(2), JdbcRealm
To access the database using JdbcRealm, first add the mysql dependency
< the dependency > < groupId > mysql < / groupId > < artifactId > mysql connector - Java < / artifactId > < version > 5.1.41 was < / version > </groupId> </artifactId> <version>1.1.6</version> </version> </dependency>Copy the code
Then write the test class JdbcRealmTest
/** * test JdbcRealm * @author Liu */ public class JdbcRealmTest {DruidDataSource = new DruidDataSource(); / / set up the database connection {dataSource. SetUrl (" JDBC: mysql: / / 127.0.0.1:3306 / shiro "); dataSource.setUsername("root"); dataSource.setPassword("1311664842"); } /** * Test JdbcRealm */ @test public void testJdbcRealm() {JdbcRealm JdbcRealm = new JdbcRealm(); // set the dataSource jdbcRealm. SetDataSource (dataSource); / / open the permission check jdbcRealm. SetPermissionsLookupEnabled (true); String sql1 = "select password from test_user where username =?" ; // jdbcRealm.setAuthenticationQuery(sql1); String sql2 = "select role_name from test_user_roles where username =?" ; // jdbcRealm.setUserRolesQuery(sql2); Sql3 = "select permission from test_roles_permissions where role_name =?" ; // jdbcRealm.setPermissionsQuery(sql3); DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager(); defaultSecurityManager.setRealm(jdbcRealm); SecurityUtils.setSecurityManager(defaultSecurityManager); Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken("CodeTiger", "123456"); subject.login(token); Println (subject.isauthenticated ()); // Check whether system.out.println (subject.isauthenticated ()) is authorized; // checkRole subject.checkrole ("admin"); Subject.checkpermission ("user:delete"); }}Copy the code
Here may not be able to help ask, our database does not have a table, how to verify it, nothing, first look at the source JdbcRealm. There are four constants at the beginning
/** * The default query used to retrieve account data for the user. */ protected static final String DEFAULT_AUTHENTICATION_QUERY = "select password from users where username = ?" ; /** * The default query used to retrieve account data for the user when {@link #saltStyle} is COLUMN. */ protected static final String DEFAULT_SALTED_AUTHENTICATION_QUERY = "select password, password_salt from users where username = ?" ; /** * The default query used to retrieve the roles that apply to a user. */ protected static final String DEFAULT_USER_ROLES_QUERY = "select role_name from user_roles where username = ?" ; /** * The default query used to retrieve permissions that apply to a particular role. */ protected static final String DEFAULT_PERMISSIONS_QUERY = "select permission from roles_permissions where role_name = ?" ;Copy the code
So all we have to do is create the corresponding tables and fields in the database with the same names.
You can also use custom queries, as commented above.
(3) Custom Realm
A custom Realm inherits from the AuthorizingRealm class and implements methods for authorization and authentication
/** * custom Realm, Public class CustomerRealm extends AuthorizingRealm {Map<String, String> map = new HashMap<>(); { map.put("CodeTiger", "123456"); // Set the name of the Realm to super.setName("customerRealm"); } /** * Authorization method */ @override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {// From the main body of authorization information for user name String username = (String) principals. GetPrimaryPrincipal (); Set<String> roles = getRolesByUsername(username); Set<String> permissions = getPermissionsByUsername(username); SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); simpleAuthorizationInfo.setRoles(roles); simpleAuthorizationInfo.setStringPermissions(permissions); return simpleAuthorizationInfo; } @param username * @return */ public Set<String> getRolesByUsername(String username) { Set<String> set = new HashSet<>(); set.add("admin"); set.add("user"); return set; } @param username * @return */ public Set<String> getPermissionsByUsername(String username) { Set<String> set = new HashSet<>(); set.add("user:delete"); set.add("user:update"); return set; } /** * Authentication method */ @override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken Token) throws AuthenticationException {// get the username from the authentication information passed by the principal String username = (String) token.getprincipal (); // 2. Query the password from the database or cache using the username. String password = getPasswordByUsername(username); if(password == null) { return null; } SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo("CodeTiger", password, "customerRealm"); return authenticationInfo; } @param username * @return */ public String getPasswordByUsername(String username) {return map.get(username); }}Copy the code
For simplicity, no data is read from the database or cache.
Then write a test class to test
public class CustomerRealmTest {
@Test
public void testCustomerRealm() {
DefaultSecurityManager defaultSecurityManager = new DefaultSecurityManager();
CustomerRealm customerRealm = new CustomerRealm();
defaultSecurityManager.setRealm(customerRealm);
SecurityUtils.setSecurityManager(defaultSecurityManager);
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken("CodeTiger", "123456");
subject.login(token);
System.out.println(subject.isAuthenticated());
subject.checkRole("admin");
subject.checkRoles("admin","user");
subject.checkPermission("user:delete");
subject.checkPermissions("user:delete", "user:update");
}
}
Copy the code