Learning task objective

  1. The user must log in to access the definition link, otherwise redirects to the login page.

  2. Control the permission of the link. Only the current login user has the permission to access the link. Otherwise, the user will jump to the specified page.

  3. Return json string information if the user enters an incorrect password or the user is set to static login.

I was using a springboot+ Mybatisplus + JSP before the construction of a basic framework. On top of that, Shiro’s integration. If you need it, you can download it from my code cloud.

Personal blog: z77z.osChina. IO /

This program download address: git.oschina.net/z77z/spring…

Import shiro dependency packages into POM.xml

Org, apache shiro shiro - spring 1.3.2Copy the code

RBAC mode is used to establish the database

RBAC is role-based Access Control in RBAC, permissions are associated with roles, and users gain permissions for these roles by becoming members of appropriate roles. This greatly simplifies permission management. In this way, the management is hierarchical and interdependent. Permissions are assigned to roles and roles are assigned to users. In this way, permissions are clearly designed and easily managed.

/* Insert TABLE structure */ DROP TABLE IF EXISTS 'u_permission'; CREATE TABLE 'u_permission' (' id 'bigint(20) NOT NULL AUTO_INCREMENT, 'url' varchar(256) DEFAULT NULL COMMENT 'u_permission ', 'name' varchar(64) DEFAULT NULL COMMENT 'url ', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=21 DEFAULT CHARSET=utf8; /*Table structure for table `u_role` */ DROP TABLE IF EXISTS `u_role`; CREATE TABLE 'u_role' (' id 'bigint(20) NOT NULL AUTO_INCREMENT,' name 'varchar(32) DEFAULT NULL COMMENT' U_role ', 'type' varchar(10) DEFAULT NULL COMMENT 'id ', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; /*Table structure for table `u_role_permission` */ DROP TABLE IF EXISTS `u_role_permission`; CREATE TABLE 'u_ROLE_PERMISSION' (' rid 'bigint(20) DEFAULT NULL COMMENT' u_roLE_permission ', 'pid' bigint(20) DEFAULT NULL COMMENT 'ID') ENGINE=InnoDB DEFAULT CHARSET=utf8; /*Table structure for table `u_user` */ DROP TABLE IF EXISTS `u_user`; CREATE TABLE 'u_user' (' id 'bigint(20) NOT NULL AUTO_INCREMENT,' nickname 'varchar(20) DEFAULT NULL COMMENT' u_user ', ` email ` varchar (128) the DEFAULT NULL COMMENT 'email | login account, ` PSWD ` varchar (32) DEFAULT NULL COMMENT' password ', 'create_time' datetime DEFAULT NULL COMMENT 'create_time ',' last_login_time 'datetime DEFAULT NULL COMMENT' last login time ', 'status' bigint(1) DEFAULT '1' COMMENT '1: valid, InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET= UTf8; /*Table structure for table `u_user_role` */ DROP TABLE IF EXISTS `u_user_role`; CREATE TABLE 'u_user_role' (' uid 'bigint(20) DEFAULT NULL COMMENT' user ID', 'rid' bigint(20) DEFAULT NULL COMMENT 'ID') ENGINE=InnoDB DEFAULT CHARSET=utf8;Copy the code

Dao layer code preparation

Entity, Service, mapper etc. of Dao layer are generated by mybatisplus code automatic generation tool, which has the function of adding, deleting, changing, searching and paging of single table, which is more convenient. I will not post the code here.

Configure shiro

ShiroConfig.java

/** * @author author z77z * @date ** / @configuration Public class ShiroConfig {/** * ShiroFilterFactoryBean handles intercepting resource files. Note that a single ShiroFilterFactoryBean configuration is or fails because it requires injection when the ShiroFilterFactoryBean is initialized: SecurityManager * * Filter Chain Definition Description 1. A URL can be configured with multiple filters, separated by commas (,). 2. roles * */ @Bean public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); / / must be set SecurityManager shiroFilterFactoryBean. SetSecurityManager (SecurityManager); / / if you do not set the default automatically find under the root directory of the Web project "/ login JSP page shiroFilterFactoryBean. SetLoginUrl ("/login"); / / to jump after a successful login link shiroFilterFactoryBean setSuccessUrl ("/index "); // Unauthorised interface; shiroFilterFactoryBean.setUnauthorizedUrl("/403"); // Interceptor. Map filterChainDefinitionMap = new LinkedHashMap(); / / configuration will not be intercepted the link order of judgment filterChainDefinitionMap. Put ("/static / * * ", "-anon"); filterChainDefinitionMap.put("/ajaxLogin", "anon"); / / configuration exit filter, of which the specific exit code Shiro has achieved filterChainDefinitionMap for us. The put ("/logout ", "logout"); FilterChainDefinitionMap. Put ("/add ", "perms/permission to add"); // This is a pit where the code will not work if you are not careful; // filterChainDefinitionMap.put("/**", "authc"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap); System.out.println("Shiro interceptor factory class injected successfully "); return shiroFilterFactoryBean; } @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); // Set realm.securityManager.setrealm (myShiroRealm()); return securityManager; } /** * authentication realm; (This needs to write by yourself, account password verification; MyShiroRealm MyShiroRealm = new MyShiroRealm(); MyShiroRealm MyShiroRealm = new MyShiroRealm MyShiroRealm(); return myShiroRealm; }}Copy the code

Login Authentication Implementation

As mentioned in the authentication, authorization internal implementation mechanism, the final processing is handed over to Real. In Shiro, user, role, and permission information in your application is ultimately obtained through Realm. Normally, Shiro’s authentication information is fetched directly from our data source in Realm. Realm is, so to speak, a DAO dedicated to the security framework.

Shiro’s authentication process is eventually handed over to a Realm, which calls the Realm’s getAuthenticationInfo(Token) method. This method performs the following operations:

1. Check the submitted token information for authentication

Get user information from a data source (usually a database) based on the token information

3. Verify user information.

4. The authentication pass returns an AuthenticationInfo instance that encapsulates the user’s information.

5. If the authentication fails, an AuthenticationException message is thrown.

In our application, we need to define a Realm class, inherit the AuthorizingRealm abstract class, override doGetAuthenticationInfo (), and override the method that gets the user information.

DoGetAuthenticationInfo rewriting

/** * Authentication information (authentication) : Authentication is used to verify user identity * * @param token * @return * @throws AuthenticationException */ @Override protected AuthenticationInfo doGetAuthenticationInfo( AuthenticationToken authcToken) throws AuthenticationException { System. Out. Println (" identity authentication methods: MyShiroRealm doGetAuthenticationInfo () "); ShiroToken token = (ShiroToken) authcToken; Map map = new HashMap(); map.put("nickname", token.getUsername()); map.put("pswd", token.getPswd()); SysUser user = null; / / retrieved from the database user name password corresponding user List userList. = sysUserService selectByMap (map); if(userList.size()! =0){ user = userList.get(0); } if (null == user) {throw new AccountException(" Incorrect account or password!" ); }else if(user.getStatus()==0){/** * if the user's status is disabled. So throw itDisabledAccountException*/ throw new DisabledAccountException(" Account is disabled!" ); }else{// Update login time last login time user.setLastLoginTime(new Date()); sysUserService.updateById(user); } return new SimpleAuthenticationInfo(user, user.getPswd(), getName()); }Copy the code

In layman's terms, this rewrite is the realization of our first learning goal.

Implementation of link permissions

Shiro's permissions are granted by inheriting the AuthorizingRealm abstract class, overloading doGetAuthorizationInfo();

This method is executed only when the link is configured with the appropriate permissions or shiro tags when accessing the page, otherwise it will not be executed, so if there is a simple authentication without permission control, this method may not be implemented and simply return NULL.

The main class used in this method is SimpleAuthorizationInfo

Add roles and permissions.

authorizationInfo.addRole(role.getRole());

authorizationInfo.addStringPermission(p.getPermission());

You can also add a set set: Roles is the role of the current user queried from the database, and stringPermissions is the permissions of the current user queried from the database

authorizationInfo.setRoles(roles);

authorizationInfo.setStringPermissions(stringPermissions);

If added filterChainDefinitionMap in shiro configuration file. The put ("/add ", "perms/permission to add"); To access /add, you must have the "add permission" permission.

If in shiro configuration files added filterChainDefinitionMap put ("/add ", "roles [100002], perms/permission to add"); To access /add, you must have the "add permission" permission and the role of "100002".

/** * Principals */ @override protected AuthorizationInfo doGetAuthorizationInfo System. The out. Println (" access authentication method: MyShiroRealm doGetAuthenticationInfo () "); SysUser token = (SysUser)SecurityUtils.getSubject().getPrincipal(); String userId = token.getId(); SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); // Query roles based on user IDS and add them into Authorization. /*Map map = new HashMap(); map.put("user_id", userId); List roleList = sysRoleService.selectByMap(map); Set roleSet = new HashSet(); for(SysRole role : roleList){ roleSet.add(role.getType()); Set roleSet = new HashSet(); Set roleSet = new HashSet(); roleSet.add("100002"); info.setRoles(roleSet); // Query permission according to user ID and add it to Authorization. /*List permissionList = sysPermissionService.selectByMap(map); Set permissionSet = new HashSet(); for(SysPermission Permission : permissionList){ permissionSet.add(Permission.getName()); }*/ Set permissionSet = new HashSet(); Permissionset.add (" add permissions "); info.setStringPermissions(permissionSet); return info; }Copy the code

The implementation of this class completes the second task of our learning goal.

Code the Web layer

Login page:

controller

@requestMapping (value="login") public String login() {return "login"; @requestMapping (value="login") public String login() {return "login"; } /** * ajax login request * @param username * @param password * @return */ @RequestMapping(value="ajaxLogin",method=RequestMethod.POST) @ResponseBody public Map submitLogin(String username, String password,Model model) { Map resultMap = new LinkedHashMap(); try { ShiroToken token = new ShiroToken(username, password); SecurityUtils.getSubject().login(token); resultMap.put("status", 200); Resultmap. put("message", "login succeeded "); } catch (Exception e) { resultMap.put("status", 500); resultMap.put("message", e.getMessage()); } return resultMap; }Copy the code

jsp

<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path; %> Login error message:

Account:

Password:

Copy the code

Home page

controller

@requestMapping (value="index") public String index() {return "index"; } @requestMapping (value="logout",method = requestmethod.get) @responseBody public Map logout(){Map  resultMap = new LinkedHashMap(); Try {// Exit securityutils.getSubject ().logout(); } catch (Exception e) { System.err.println(e.getMessage()); } return resultMap; }Copy the code

jsp


<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%
    String path = request.getContextPath();
    String basePath = request.getScheme() + "://"
            + request.getServerName() + ":" + request.getServerPort()
            + path;
%>





Insert title here


    helloJsp
    



Copy the code

Add operation page

controller


@RequestMapping(value="add")
public String add() {
    return "add";
}
Copy the code

jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path; %> Insert title here has add permissionCopy the code

test

Task a

After writing, you can start the program, access the Index page, because there is no login will jump to the login page.

After you login, you will be redirected to the index page. After you log out, you will be redirected to the login page by entering the index page in the browser

Above these operating time trigger MyShiroRealm. DoGetAuthenticationInfo () this method, namely login authentication methods.


Task 2

After login, the add page is successfully accessed. In the Shiro configuration file, change the add access to

FilterChainDefinitionMap. Put ("/add ", "perms/permission to delete");

A 404 error is reported because 403 page was not written.

These actions above, will trigger the authority authentication methods: MyShiroRealm. DoGetAuthorizationInfo (), will trigger a once per visit.


Task three

If you enter an incorrect user name or password, the system displays "Incorrect user name or password! In the database, change the status of a user to disabled, and then log in, the message "account has been disabled!" Error message

The above operation, is in MyShiroRealm doGetAuthenticationInfo () login authentication method is implemented, by querying the database to judge whether the current logged in user is disabled, the concrete can go to the source code.

conclusion

Of course shiro is very powerful, it only completes the login authentication and permission management functions, I will continue to learn and share, talk about the next learning path:

  1. Shiro + redis integration, avoid every visit to have permission to link to execute MyShiroRealm. DoGetAuthenticationInfo () method to check the current user permissions, because permissions in actual situation is not often become, so that you can use redis permissions cache.

  2. Shiro link permissions dynamic loading, before permission to add a link, to add filterChainDefinitionMap in shiro configuration file. The put ("/add ", "roles [100002], perms [permissions to add]"), it is not very convenient management, One way is to load the linked permissions using the database, and the other way is to read them through an init configuration file.

  3. Shiro custom permission check Filter definition, and functional implementation.

  4. Shiro Ajax request permission not satisfied, after interception solution. The premise is that Ajax can't redirect and forward pages, so if an Ajax request is not logged in, it will give the user the impression that there is no response and the user does not know that the user has logged out.

  5. Shiro JSP tag usage.

  6. Shiro logs in and jumps to the last page visited

  7. Online display, online user management (kick out login).

  8. The login registration password is encrypted and transmitted.

  9. Integration verification code.

  10. Remember my function. You are still logged in after closing the browser.

  11. There are not thought of later, welcome to put forward some suggestions.

Last Updated:



If you want to reprint, please indicate the source:
Z77z. Oschina. IO / 2017/02/13 /...