Spring Web integrates Shiro


Apache Shiro is a powerful Java security framework that provides authentication, authorization, encryption, and more to protect your applications from malicious attacks. In this article, we will describe how to integrate Apache Shiro into a Spring web application.

1. Add dependencies

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

2. Configure Shiro

<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
    <property name="realm" ref="myRealm"/>
</bean>

<bean id="myRealm" class="com.example.MyRealm">
    <!-- 设置密码匹配器 -->
    <property name="credentialsMatcher">
        <bean class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
            <property name="hashAlgorithmName" value="SHA-256"/>
            <property name="hashIterations" value="1000"/>
        </bean>
    </property>
</bean>

<bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
    <property name="securityManager" ref="securityManager"/>
    <property name="loginUrl" value="/login"/>
    <property name="successUrl" value="/home"/>
    <property name="unauthorizedUrl" value="/unauthorized"/>

    <property name="filterChainDefinitions">
        <value>
            /login = anon
            /logout = logout
            /static/** = anon
            /** = authc
        </value>
    </property>
</bean>

In the configuration above, we defined one SecurityManagerand one Realm for handling authentication and authorization requests. We also define one ShiroFilterFactoryBeanfor creating a ShiroFilterFilter that will handle all requests.

We pass all requests through the authc filter, which means only authenticated users can access them. We also define some exceptions like /loginand /static/**, these paths will not require authentication.

3. Create Realm

Next, we need to create a Realm to handle authentication and authorization requests. Here is an example Realm implementation:

public class MyRealm extends AuthorizingRealm {
    
    

    @Autowired
    private UserService userService;

    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
    
    
        String username = (String) principals.getPrimaryPrincipal();

        // 从数据库中获取用户的角色和权限信息
        Set<String> roles = userService.findRolesByUsername(username);
        Set<String> permissions = userService.findPermissionsByUsername(username);

        SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        authorizationInfo.setRoles(roles);
        authorizationInfo.setStringPermissions(permissions);

        return authorizationInfo;
    }

    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    
    
           String username = (String) token.getPrincipal();
    String password = new String((char[]) token.getCredentials());

    // 从数据库中获取用户信息
    User user = userService.findByUsername(username);

    // 如果用户不存在,则抛出异常
    if (user == null) {
    
    
        throw new UnknownAccountException("用户不存在");
    }

    // 验证用户密码是否正确
    if (!password.equals(user.getPassword())) {
    
    
        throw new IncorrectCredentialsException("用户名或密码错误");
    }

    SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(username, password, getName());
    return authenticationInfo;
}


In this Realm, we implement doGetAuthorizationInfoand doGetAuthenticationInfotwo methods.

doGetAuthorizationInfoUsed to obtain the user's role and permission information, which can be obtained from the database or other data sources. In this example, we get the user's roles and permissions by calling a UserService.

doGetAuthenticationInfoUsed to authenticate the user. In this example, we fetch the user's information from the database and compare if the passwords match. If the passwords do not match, an exception is thrown.

Fourth, write the controller

Now that we have Shiro set up, we need to write some controllers to handle requests. Here is an example controller:

@Controller
public class HomeController {
    
    

    @RequestMapping("/home")
    public String home() {
    
    
        return "home";
    }

    @RequestMapping("/login")
    public String login() {
    
    
        return "login";
    }

    @RequestMapping("/logout")
    public String logout() {
    
    
        SecurityUtils.getSubject().logout();
        return "redirect:/login";
    }

    @RequestMapping("/unauthorized")
    public String unauthorized() {
    
    
        return "unauthorized";
    }
}

In this example, we define a HomeController, which contains some methods to handle requests. /homemethod will return a home view, /loginmethod will return a login view, /logoutmethod will log out the user and redirect to the login view, /unauthorizedmethod will return a unauthorizedview.

5. Create a view

<html>
  <body>
    <h1>Login</h1>
    <form method="post" action="/login">
      <label for="username">Username:</label>
      <input type="text" id="username" name="username"/><br/>
      <label for="password">Password:</label>
      <input type="password" id="password" name="password"/><br/>
      <input type="submit" value="Login"/>
    </form>
  </body>
</html>

In this example we create a simple login form where the user enters their username and password and the form is submitted to the /loginroute.

6. Configure Shiro

The final step is to configure Shiro in Spring. We need to create a ShiroConfig class for configuring Shiro and Spring integration. Here is an example ShiroConfig class:

@Configuration
public class ShiroConfig {
    
    

    @Bean
    public ShiroFilterFactoryBean shiroFilter() {
    
    
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        shiroFilter.setSecurityManager(securityManager());
        shiroFilter.setLoginUrl("/login");
        shiroFilter.setSuccessUrl("/home");
        shiroFilter.setUnauthorizedUrl("/unauthorized");

        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/logout", "logout");
        filterChainDefinitionMap.put("/**", "authc");

        shiroFilter.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return shiroFilter;
    }

    @Bean
    public DefaultWebSecurityManager securityManager() {
    
    
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm());
        return securityManager;
    }

    @Bean
    public UserRealm realm() {
    
    
        UserRealm realm = new UserRealm();
        return realm;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
    
    
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(securityManager());
        return advisor;
    }
}

In this example, we define a ShiroFilterFactoryBeanand a DefaultWebSecurityManager, which are used to configure the Shiro filter and security manager. We also define a UserRealm, which is used to obtain the user's role and permission information.

In ShiroFilterFactoryBean, we define a filter chain for mapping requests to corresponding filters. In this example, we define a /login path for anonymous access, a /logout path for logout, and an authc filter for authentication. We also define default URLs for successful logins, and URLs for unauthorized ones.

In DefaultWebSecurityManager, we set what we defined UserRealm.

Finally, we define a AuthorizationAttributeSourceAdvisor, which enables annotation-based authorization. @RequiresRolesThis will allow us to restrict access using and @RequiresPermissionsannotations on controller methods .

7. Test

Now that we've finished integrating and configuring Shiro, we can start the application and test it. Access in your browser http://localhost:8080/login, enter your username and password, and click the Login button. If the username and password are correct, you will be redirected to http://localhost:8080/home. If the username or password is incorrect, you will receive an error message.

If you try to access /homepath, you will be redirected to http://localhost:8080/loginpath because you are not yet authenticated. If you log out and try to access the /home path again, you will get an unauthorized error message.

Guess you like

Origin blog.csdn.net/qq_54351538/article/details/129354361