船志健康项目-移动端开发-手机快速登录、权限控制9

一、需求分析

 手机快速登录功能,就是通过短信验证码的方式进行登录。这种方式相对于用户名密码登录方式,用户不需要记忆自己的密码,只需要通过输入手机号并获取验证码就可以完成登录,是目前比较流行的登录方式。

  image  image

二、手机快速登录

1. 页面调整

 登录页面为/pages/login.html

 1.1 发送验证码

  为获取验证码按钮绑定事件,并在事件对应的处理函数中校验手机号,如果手机号输入正确则显示30秒倒计时效果并发送ajax请求,发送短信验证码

<div class="input-row">
  <label>手机号</label>
  <div class="loginInput">
    <input v-model="loginInfo.telephone" id='account' type="text" placeholder="请输入手机号">
    <input id="validateCodeButton" @click="sendValidateCode()" type="button" style="font-size: 12px" value="获取验证码">
  </div>
</div>
    <script>
        var vue = new Vue({
            el:'#app',
            data:{
                loginInfo:{}//登录信息
            },
            methods:{
                //发送验证码
                sendValidateCode(){
                    var telephone = this.loginInfo.telephone;
                    //对手机号进行合法性校验
                    if (!checkTelephone(telephone)) {
                        this.$message.error('请输入正确的手机号');
                        return false;
                    }
                    validateCodeButton = $("#validateCodeButton")[0];
                    clock = window.setInterval(doLoop, 1000); //一秒执行一次
                    //发送ajax请求,发送手机验证码
                    axios.post("/validateCode/send4Login.do?telephone=" + telephone).then((response) => {
                        if(!response.data.flag){
                            //验证码发送失败
                            this.$message.error('验证码发送失败,请检查手机号输入是否正确');
                        }
                    });
                }
            }
        });
    </script>

  在ValidateCodeController中提供send4Login方法,调用短信服务发送验证码并将验证码保存到redis

    //用户在线体检预约发送验证码
    @RequestMapping("/send4Login")
    public Result send4Login(String telephone){
        //随机生成6位数字验证码
        Integer validateCode = ValidateCodeUtils.generateValidateCode(6);
        //给用户发送验证码
        try {
            SMSUtils.sendShortMessage(SMSUtils.VALIDATE_CODE,telephone,validateCode.toString());
        } catch (ClientException e) {
            e.printStackTrace();
            //验证码发送失败
            //return new Result(false, MessageConstant.SEND_VALIDATECODE_FAIL);
        }
        System.out.println("send validateCode is :"+validateCode);
        //将生成的验证码缓存到redis,5分钟后失效
        jedisPool.getResource().setex(telephone+ RedisMessageConstant.SENDTYPE_LOGIN,5*60,validateCode.toString());
        return new Result(true,MessageConstant.SEND_VALIDATECODE_SUCCESS);
    }

 1.2 提交登录请求

  为登录按钮绑定事件

<div class="btn yes-btn"><a @click="login()" href="#">登录</a></div>
                //登录
                login(){
                    var telephone = this.loginInfo.telephone;
                    if (!checkTelephone(telephone)) {
                        this.$message.error('请输入正确的手机号');
                        return false;
                    }
                    //发送ajax请求,将表单数据提交到Controller进行登录处理
                    axios.post("/member/login.do",this.loginInfo).then((response) => {
                        if(response.data.flag){
                            //登录成功,跳转到会员首页
                            window.location.href="member.html";
                        }else{
                            //失败,提示失败信息
                            this.$message.error(response.data.message);
                        }
                    });
                }

2. 后台代码

 2.1 Controller

  在health_mobile工程中创建MemberController并提供login方法进行登录检查,处理逻辑为:

   1、校验用户输入的短信验证码是否正确,如果验证码错误则登录失败

   2、如果验证码正确,则判断当前用户是否为会员,如果不是会员则自动完成会员注册

   3、向客户端写入Cookie,内容为用户手机号

   4、将会员信息保存到Redis,使用手机号作为key,保存时长为30分钟

package com.itheima.controller;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.itheima.constant.MessageConstant;
import com.itheima.constant.RedisMessageConstant;
import com.itheima.entity.Result;
import com.itheima.pojo.Member;
import com.itheima.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import redis.clients.jedis.JedisPool;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.Map;

/**
 * 处理会员相关操作
 * 会员登录
 */
@RestController
@RequestMapping("/member")
public class MemberController {

    @Reference
    private MemberService memberService;

    @Autowired
    private JedisPool jedisPool;

    @RequestMapping("/login")
    public Result login(HttpServletResponse response, @RequestBody Map map){
        String telephone = (String) map.get("telephone");
        String validateCode = (String) map.get("validateCode");

        //从Redis中获取缓存的验证码,key为手机号+RedisMessageConstant.SENDTYPE_LOGIN
        String validateCodeInRedis = jedisPool.getResource().get(telephone + RedisMessageConstant.SENDTYPE_LOGIN);

        //校验手机验证码,将用户输入的验证码和Redis中保存的验证码进行比对
        if(validateCodeInRedis == null || !validateCodeInRedis.equals(validateCode)){
       //验证码输入错误 return new Result(false, MessageConstant.VALIDATECODE_ERROR); }
     //验证码输入正确 //检查当前用户是否为会员,根据手机号判断,如果不是会员则自动完成注册 Member member = memberService.findByTelephone(telephone); if(member == null){ //用户不是会员,需要添加到会员表,完成注册 member = new Member(); member.setRegTime(new Date()); member.setPhoneNumber(telephone); memberService.add(member); } //登录成功 //向客户端浏览器写入Cookie,内容为手机号,跟踪用户 Cookie cookie = new Cookie("login_member_telephone",telephone); cookie.setPath("/");//路径 cookie.setMaxAge(60*60*24*30);//有效期30天 response.addCookie(cookie); //保存会员信息到Redis中 String json = JSON.toJSON(member).toString(); jedisPool.getResource().setex(telephone,30*60,json); return new Result(true,MessageConstant.LOGIN_SUCCESS); } }
 2.2 服务接口
  在MemberService服务接口中提供findByTelephone和add方法
    //根据手机号查询会员
    public Member findByTelephone(String telephone);
    public void add(Member member);

 2.3 服务实现类

  在MemberServiceImpl服务实现类中实现findByTelephone和add方法
    @Override
    public Member findByTelephone(String telephone) {
        return memberDao.findByTelephone(telephone);
    }

    @Override
    public void add(Member member) {
        String password = member.getPassword();

        if(password != null){
            //使用md5将明文密码进行加密
            password = MD5Utils.md5(password);
            member.setPassword(password);
        }
        memberDao.add(member);
    }

 2.4 Dao接口

  在MemberDao接口中声明findByTelephone和add方法
public Member findByTelephone(String telephone);
public void add(Member member);

 2.5 Mapper映射文件

  在MemberDao.xml映射文件中定义SQL语句
    <!--根据手机号查询会员-->
    <select id="findByTelephone" parameterType="string" resultType="com.itheima.pojo.Member">
        select * from t_member where phoneNumber = #{phoneNumber}
    </select>

    <!--新增会员-->
    <insert id="add" parameterType="com.itheima.pojo.Member">
        <selectKey resultType="java.lang.Integer" order="AFTER" keyProperty="id">
            SELECT LAST_INSERT_ID()
        </selectKey>
        insert into t_member(fileNumber,name,sex,idCard,phoneNumber,regTime,password,email,birthday,remark)
        values (#{fileNumber},#{name},#{sex},#{idCard},#{phoneNumber},#{regTime},#{password},#{email},#{birthday},#{remark})
    </insert>

 三、权限控制

1. 认证和授权概念

 前面我们已经完成了传智健康后台管理系统的部分功能,例如检查项管理、检查组管理、套餐管理、预约设置等。接下来我们需要思考2个问题:

 问题1:在生产环境下我们如果不登录后台系统就可以完成这些功能操作吗?

  答案显然是否定的,要操作这些功能必须首先登录到系统才可以。

 问题2:是不是所有用户,只要登录成功就都可以操作所有功能呢?

  答案是否定的,并不是所有的用户都可以操作这些功能。不同的用户可能拥有不同的权限,这就需要进行授权了。

 

 认证:系统提供的用于识别用户身份的功能,通常提供用户名和密码进行登录其实就是在进行认证,认证的目的是让系统知道你是谁。

 授权:用户认证成功后,需要为用户授权,其实就是指定当前用户可以操作哪些功能。

 本章节就是要对后台系统进行权限控制,其本质就是对用户进行认证和授权。

2. 权限模块数据模型

 前面已经分析了认证和授权的概念,要实现最终的权限控制,需要有一套表结构支撑:

  用户表t_user、权限表t_permission、角色表t_role、菜单表t_menu、用户角色关系表t_user_role、角色权限关系表t_role_permission、角色菜单关系表t_role_menu。

 表之间关系如下图:

 image

 通过上图可以看到,权限模块共涉及到7张表。在这7张表中,角色表起到了至关重要的作用,其处于核心位置,因为用户、权限、菜单都和角色是多对多关系。

 接下来我们可以分析一下在认证和授权过程中分别会使用到哪些表:

  认证过程:只需要用户表就可以了,在用户登录时可以查询用户表t_user进行校验,判断用户输入的用户名和密码是否正确。

  授权过程:用户必须完成认证之后才可以进行授权,首先可以根据用户查询其角色,再根据角色查询对应的菜单,这样就确定了用户能够看到哪些菜单。然后再根据用户的角色查询对应的权限,这样就确定了用户拥有哪些权限。所以授权过程会用到上面7张表。

3. 在项目中应用Spring Security

 将Spring Security框架应用到 后台系统中进行权限控制,其本质就是认证和授权。

 在真正进行认证和授权之前需要对表里的数据进行管理,即我们需要开发如下一些功能:

  1、权限数据管理(增删改查)

  2、菜单数据管理(增删改查)

  3、角色数据管理(增删改查、角色关联权限、角色关联菜单)

  4、用户数据管理(增删改查、用户关联角色)

 鉴于时间关系,我们不再实现这些数据管理的代码开发。我们可以直接将数据导入到数据库中即可。 

 3.1 导入Spring Security环境

  第一步:在health_parent父工程的pom.xml中导入Spring Security的maven坐标(添加到dependencyManagement中)

            <!-- 安全框架 -->
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-web</artifactId>
                <version>${spring.security.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-config</artifactId>
                <version>${spring.security.version}</version>
            </dependency>
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-taglibs</artifactId>
                <version>${spring.security.version}</version>
            </dependency>

  第二步:在health_backend工程的web.xml文件中配置用于整合Spring Security框架的过滤器DelegatingFilterProxy

  <filter>
    <!--
      DelegatingFilterProxy委派过滤器,用于整合第三方框架
      整合Spring Security时过滤器的名称必须为springSecurityFilterChain,否则会抛出NoSuchBeanDefinitionException异常
    -->
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <!--访问index.html就会进入到springsecurity框架-->
    <url-pattern>/*</url-pattern>
  </filter-mapping>

 3.2 实现认证和授权

  第一步:在health_backend工程中按照Spring Security框架要求提供SpringSecurityUserService,并且实现UserDetailsService接口

package com.itheima.security;

import com.alibaba.dubbo.config.annotation.Reference;
import com.itheima.pojo.Permission;
import com.itheima.pojo.Role;
import com.itheima.service.UserService;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Component
public class SpringSecurityUserService implements UserDetailsService {

    //使用dubbo通过网络远程调用服务提供方获取数据库中的用户信息
    @Reference
    private UserService userService;


    //根据用户名查询数据库获取用户信息
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        System.out.println("username:"+username);
        com.itheima.pojo.User user = userService.findByUsername(username);
        if(user == null){//用户不存在
            return null;
        }
        //动态为用户授权
        List<GrantedAuthority> list = new ArrayList<>();
        Set<Role> roles = user.getRoles();
        for (Role role : roles) {
            //授予角色
            list.add(new SimpleGrantedAuthority(role.getKeyword()));
            Set<Permission> permissions = role.getPermissions();
            for (Permission permission : permissions) {
                //授权
                list.add(new SimpleGrantedAuthority(permission.getKeyword()));
            }
        }

        UserDetails userDetails = new User(username,user.getPassword(),list);
        //将用户信息返回给框架,框架会进行密码比对(页面提交的密码和数据库中查询的密码进行比对)
        return userDetails;
    }
}

  第二步:创建UserService服务接口、服务实现类、Dao接口、Mapper映射文件等(health_interface)

package com.itheima.service;

import com.itheima.pojo.User;
/**
 * 用户服务接口
 */
public interface UserService {
    public User findByUsername(String username);
}
package com.itheima.service.impl;

import com.alibaba.dubbo.config.annotation.Service;
import com.itheima.dao.PermissionDao;
import com.itheima.dao.RoleDao;
import com.itheima.dao.UserDao;
import com.itheima.pojo.Permission;
import com.itheima.pojo.Role;
import com.itheima.pojo.User;
import com.itheima.service.SetmealService;
import com.itheima.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import java.util.Set;

@Service(interfaceClass = UserService.class)
@Transactional
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDao userDao;

    @Autowired
    private RoleDao roleDao;

    @Autowired
    private PermissionDao permissionDao;

    //根据用户名查询数据库获取用户信息、角色信息、权限信息
    @Override
    public User findByUsername(String username) {

        //查询用户基本信息,不包含用户的角色
        User user = userDao.findByUsername(username);
        if(user == null){
            return null;
        }

        Integer userId = user.getId();
        //根据用户id查询对应的角色
        Set<Role> roles = roleDao.findByUserId(userId);
        if(roles != null && roles.size() > 0){
            for (Role role : roles) {
                Integer roleId = role.getId();
                //根据角色id查询对应的权限
                Set<Permission> permissions = permissionDao.findByRoleId(roleId);
                if(permissions != null && permissions.size() > 0){
                    role.setPermissions(permissions);
                }
            }
            user.setRoles(roles);
        }
        return user;
    }
}
package com.itheima.dao;

import com.itheima.pojo.User;

public interface UserDao {
    public User findByUsername(String username);
}
package com.itheima.dao;

import com.itheima.pojo.Role;
import java.util.Set;

public interface RoleDao {
    public Set<Role> findByUserId(Integer userId);
}
package com.itheima.dao;

import com.itheima.pojo.Permission;
import java.util.Set;

public interface PermissionDao {
    public Set<Permission> findByRoleId(Integer roleId);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.itheima.dao.UserDao" >
    <select id="findByUsername" parameterType="String" resultType="com.itheima.pojo.User">
        select * from t_user where username = #{username}
    </select>
</mapper>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.itheima.dao.RoleDao" >
    <!--根据用户id查询关联的角色-->
    <select id="findByUserId" parameterType="int" resultType="com.itheima.pojo.Role">
        select r.* from t_role r, t_user_role ur where r.id = ur.role_id and ur.user_id = #{userId}
    </select>
</mapper>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.itheima.dao.PermissionDao" >
    <!--根据角色id查询关联的权限-->
    <select id="findByRoleId" parameterType="int" resultType="com.itheima.pojo.Permission">
        select p.* from t_permission p, t_role_permission rp where p.id = rp.permission_id and rp.role_id = #{roleId}
    </select>
</mapper>

  第三步:修改health_backend工程中的springmvc.xml文件,修改dubbo批量扫描的包路径

<!--批量扫描-->
<dubbo:annotation package="com.itheima" />

   注意:此处原来扫描的包为com.itheima.controller,现在改为com.itheima包的目的是需要将我们上面定义的SpringSecurityUserService也扫描到,因为在SpringSecurityUserService的 loadUserByUsername方法中需要通过dubbo远程调用名称为UserService的服务。

  第四步:在health_backend工程中提供spring-security.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:security="http://www.springframework.org/schema/security"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans.xsd
                        http://www.springframework.org/schema/mvc
                        http://www.springframework.org/schema/mvc/spring-mvc.xsd
                        http://code.alibabatech.com/schema/dubbo
                        http://code.alibabatech.com/schema/dubbo/dubbo.xsd
                        http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context.xsd
                     http://www.springframework.org/schema/security
                     http://www.springframework.org/schema/security/spring-security.xsd">

    <!--配置哪些资源匿名可以访问,不需要进行权限校验,即不登录可以访问-->
    <security:http security="none" pattern="/login.html" />
    <security:http security="none" pattern="/css/**" />
    <security:http security="none" pattern="/js/**" />
    <security:http security="none" pattern="/img/**" />
    <security:http security="none" pattern="/plugins/**" />

    <!--        
        http:用于定义相关权限控制        
        auto-config:是否自动配置                        
                    设置为true时框架会提供默认的一些配置,例如提供默认的登录页面、登出处理等                        
                    设置为false时需要显示提供登录表单配置,否则会报错        
        use-expressions:是否适用spring security提供的表达式来描述权限。用于指定intercept-url中的access属性是否使用表达式    
    -->
    <security:http auto-config="true" use-expressions="true">
        <security:headers>
            <!--设置在页面可以通过iframe访问受保护的页面,默认为不允许访问,main.html是通过iframe嵌套的-->
            <security:frame-options policy="SAMEORIGIN" />
        </security:headers>

        <!--只要认证通过就可以访问-->
        <security:intercept-url pattern="/pages/**" access="isAuthenticated()" />
        <!--如果要使用自己指定的页面作为登录页面,必须配置登录表单,页面提交的登录表单请求是由框架负责处理
            login-page:指定登录页面访问url
            login-processing-url:对应表单action请求地址
            default-target-url:登录认证成功后跳转的url
            authentication-failure-url:认证失败后跳转的url
        -->
        <security:form-login login-page="/login.html"
                             username-parameter="username"
                             password-parameter="password"
                             login-processing-url="/login.do"
                             default-target-url="/pages/main.html"
                             always-use-default-target="true"
                             authentication-failure-url="/login.html"
        />
        <!--  
            csrf:对应CsrfFilter过滤器  
            disabled:是否启用CsrfFilter过滤器,如果使用自定义登录页面需要关闭此项,否则登录操作会被禁用(403)-->
        <security:csrf disabled="true"></security:csrf>
        <!--  
            logout:退出登录  
            logout-url:退出登录操作对应的请求路径  
            logout-success-url:退出登录后的跳转页面
            invalidate-session:将当前session失效
        -->
        <security:logout logout-url="/logout.do" logout-success-url="/login.html" invalidate-session="true"/>
    </security:http>

    <!--配置密码加密对象-->
    <bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder" />

    <!--authentication-manager:认证管理器,用于处理认证操作-->
    <security:authentication-manager>
        <!--authentication-provider:认证提供者,执行具体的认证逻辑-->
        <!--user-service-ref:用于获取用户信息,提供给authentication-provider进行认证-->
        <security:authentication-provider user-service-ref="springSecurityUserService">
            <!--指定密码加密策略-->
            <security:password-encoder ref="passwordEncoder" />
        </security:authentication-provider>
    </security:authentication-manager>

    <!--开启注解方式权限控制-->
    <security:global-method-security pre-post-annotations="enabled" />

</beans>

  第五步:在springmvc.xml文件中引入spring-security.xml文件

<import resource="spring-security.xml"/>

  第六步:在Controller的方法上加入权限控制注解,此处以CheckItemController为例

package com.itheima.controller;

import com.alibaba.dubbo.config.annotation.Reference;
import com.itheima.constant.MessageConstant;
import com.itheima.entity.PageResult;
import com.itheima.entity.QueryPageBean;
import com.itheima.entity.Result;
import com.itheima.pojo.CheckItem;
import com.itheima.service.CheckItemService;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;

/**
 * 检查项管理
 */
@RestController
@RequestMapping("/checkitem")
public class CheckItemController {

    @Reference //到zookeeper注册中心查找服务
    private CheckItemService checkItemService;

    //新增检查项
    @PreAuthorize("hasAuthority('CHECKITEM_ADD')")//权限校验
    @RequestMapping(value = "/add",method = RequestMethod.POST)
    public Result add(@RequestBody CheckItem checkItem){
        try{
            checkItemService.add(checkItem);
        }catch (Exception e){
            e.printStackTrace();
            return new Result(false, MessageConstant.ADD_CHECKITEM_FAIL);
        }
        return new Result(true,MessageConstant.ADD_CHECKITEM_SUCCESS);
    }

    //检查项分页查询
    @PreAuthorize("hasAuthority('CHECKITEM_QUERY')")//权限校验
    @RequestMapping("/findPage")
    public PageResult findPage(@RequestBody QueryPageBean queryPageBean){
        PageResult pageResult = checkItemService.pageQuery(queryPageBean);
        return pageResult;
    }

    //删除检查项
    @RequestMapping("/delete")
    @PreAuthorize("hasAuthority('CHECKITEM_DELETE')")   //权限校验
    public Result delete(Integer id){
        try{
            checkItemService.delete(id);
        }catch (Exception e){
            e.printStackTrace();
            return new Result(false, MessageConstant.DELETE_CHECKITEM_FAIL);
        }
        return new Result(true,MessageConstant.DELETE_CHECKITEM_SUCCESS);
    }

    @PreAuthorize("hasAuthority('CHECKITEM_EDIT')")//权限校验
    @PostMapping("/edit")
    public Result edit(@RequestBody CheckItem checkItem){
        try{
            checkItemService.edit(checkItem);
        }catch (Exception e){
            return new Result(false,MessageConstant.EDIT_CHECKITEM_FAIL);
        }
        return new Result(true,MessageConstant.EDIT_CHECKITEM_SUCCESS);
    }


}
View Code

  第七步:修改页面,没有权限时提示信息设置,此处以checkitem.html中的handleDelete方法为例

//权限不足提示
showMessage(r){
  if(r == 'Error: Request failed with status code 403'){
    //权限不足
    this.$message.error('无访问权限');
    return;
 }else{
    this.$message.error('未知错误');
    return;
 }
}
                // 删除
                handleDelete(row) {//row其实是一个json对象,json对象的结构为{}
                    this.$confirm("确认删除当前选中记录吗?","提示",{
                        type:'warning'
                    }).then(()=>{
                        //点击确定按钮,发送ajax请求,将检查项交给Controller进行处理
                        axios.get("/checkitem/delete.do?id="+row.id).then((response)=>{
                            if(response.data.flag){
                                //删除成功
                                this.$message({
                                    message: response.data.message,
                                    type: 'success'
                                });
                                //调用分页,获取最新分页数据
                                this.findPage();
                            }else{
                                //删除失败
                                this.$message.error(response.data.message);
                            }
                        }).catch((r)=>{
                            this.showMessage(r);
                        });
                    }).catch(() => {
                        //点击取消按钮
                        this.$message({
                            message: "操作已取消",
                            type: 'info'
                        });
                    });
                }

 3.3 显示用户名

  前面我们已经完成了认证和授权操作,如果用户认证成功后需要在页面展示当前用户的用户名。Spring Security在认证成功后会将用户信息保存到框架提供的上下文对象中,所以此处我们就可以调用Spring Security框架提供的API获取当前用户的username并展示到页面上。 

  实现步骤:

   第一步:在health_backend工程main.html页面中修改,定义username模型数据基于VUE的数据绑定展示用户名,发送ajax 请求获取username

<script>
    new Vue({
        el: '#app',
        data:{
            username:null,  //用户名
            menuList:[]
        },
        created(){
            //发送ajax请求获取当前登录用户名
            axios.get("/user/getUsername.do").then((res)=>{
                if(res.data.flag){
                    this.username = res.data.data;
                }
            });
        }
    });
</script>
<div class="avatar-wrapper">
  <img src="../img/user2-160x160.jpg" class="user-avatar">
  <!--展示用户名-->
 {{username}}
</div>

   第二步:创建UserController并提供getUsername方法

package com.itheima.controller;

import com.itheima.constant.MessageConstant;
import com.itheima.entity.Result;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 登录用户
 */
@RestController
@RequestMapping("/user")
public class UserController {

    //获取当前登录用户的用户名
    @RequestMapping("/getUsername")
    public Result getUsername(){
        //当Spring Security完成认证后,会将当前用户信息保存到框架提供的上下文对象
        try{
            User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
            System.out.println("user: "+user);
            return new Result(true, MessageConstant.GET_USERNAME_SUCCESS,user.getUsername());
        }catch (Exception e){
            return new Result(false, MessageConstant.GET_USERNAME_FAIL);
        }
    }
}

    通过debug调试可以看到Spring Security框架在其上下文中保存的用户相关信息:

    image

 3.4 用户退出

   第一步:在main.html中提供的退出菜单上加入超链接

<el-dropdown-item divided>
  <span style="display:block;"><a href="/logout.do">退出</a></span>
</el-dropdown-item>

  第二步:在spring-security.xml文件中配置

        <!--  
            logout:退出登录  
            logout-url:退出登录操作对应的请求路径  
            logout-success-url:退出登录后的跳转页面
            invalidate-session:将当前session失效
        -->
        <security:logout logout-url="/logout.do" logout-success-url="/login.html" invalidate-session="true"/>

 

posted on 2025-11-29 01:40  花溪月影  阅读(1)  评论(0)    收藏  举报