周刚vk

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

现在开始我们就可以写登录相关的东西了。首先登录相关的流程是这样的,前端输入用户和密码传给后端,后端判断用户名和密码是否正确,若正确,则生成JWT令牌,若不正确,则需要让前端重新输入,前端如果拿到了JWT令牌,则会将令牌放在请求头里面,后面的每一次请求都会携带这个JWT令牌。然后,我们会写一个JWT相关的令牌拦截器。每次请求之前,先走一遍拦截器,判断一下当前的令牌是否存在,和令牌是否正确合法有效。若不存在或者并不是合法有效的,我们肯定会进行拦截,不会让其访问其他的接口,若合法,则让其访问接口。

我们现在实现第一个接口。前端传用户名和密码。我们根据传入的值判断是否正确,正确则生成JWT令牌。

首先我们在Pojo下面的Admin类下,让其集成UserDetails这个类。也可以专门写一个类去实现UserDetails,因为我们现在是springsecurity框架,该框架里面真正登录的方法是使用UserDetailsService类里面的一个方法LoginUserByUserName()。登录之后返回的就是UserDetails。

然后在实现UserDetailsService类时,要重写几个方法。

 @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return enabled;
    }

 这里要把IsAccountNonExpired(),isAccountNonLocked(),isCredentialsNoExpired()的返回值改成true,否则会登录失败。把getAuthorities()方法的返回值改成Null.这里是关于权限的,先空着。对于isEnabled()方法。因为admin类中已经有了enabled的属性了,所以直接返回enabled就可以了。

 

这时,我们在pojo中还要再新建一个类,AdminLoginParam用户登录实体类,这个类专门用来传递前端传过来的用户名和密码。这里我们无需使用admin类,因为登录只需要传递用户名和密码就可以。

实现添加lombook的三个注解,@Data,@EqualsAndHashCode,@Accessors(chain = true)

然后再添加Swgger的注解 @ApiModel(value="AdminLogin对象",description="")

 

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;

/**
 * @Author: zhougang
 * @Date: 2021/11/9
 * @Time: 19:26
 * @Description:用户登录实例类
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@ApiModel(value = "AdminLogin对象",description = "")
public class AdminLoginParam {
    @ApiModelProperty(value = "用户名",required = true)
    private String username;
    @ApiModelProperty(value = "密码",required = true)
    private String password;

} 

 这里有2个属性,用户名和密码,在上面添加了@ApiModelProperty注解。

上述类建造完成。

这时候我们就开始进行我们的登录功能。可以选择先写Controller再写Service,再写Mapper,也可以选择从后面往前面写,都可以。我们这里先从前面王后面写。

首先我们在controller中新建一个LoginController。加上@RestController注解,然后加上swagger注解@Api(tags = "LoginController")

后面我们就不再用注释直接使用swagger作为注释了。

@Api(tags = "LoginController")
@RestController
public class LoginController {
    @Autowired
    private IAdminService adminService;

    @ApiOperation(value="登录之后返回token")
    @PostMapping("/login")
    public RespBean login(AdminLoginParam adminLoginParam, HttpServletRequest request) {
        return adminService.login(adminLoginParam.getUsername(), adminLoginParam.getPassword(), request);
    }
} 

这样上述的controller就已经写完了,然后我们去写上述方法中调用的login接口。 

public interface IAdminService extends IService<Admin> {
    /**
     * 登录之后返回token
     * @param username
     * @param password
     * @param request
     * @return
     */
    RespBean login(String username, String password, HttpServletRequest request);
} 

写完上面的接口之后,我们要撰写其实现类AdminServiceImpl,使用springsecurity中的userdetails类中的loaduserbyusername实现登录

1.因为要使用到UserDetails所以我们需要先注入 UserDetailsService

2.使用UserDetailsService中的LoadUserByUsername()方法获取UserDetails

3.判断当前获取的到UserDetails是否存在,然后注入PasswordEncoder。使用PasswordEncoder类的maches方法将 前端传入的password和userDetails.getPassword()进行匹配。

    @Override
    public RespBean login(String username, String password, HttpServletRequest request) {
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);
        if (null == userDetails || !passwordEncoder.matches(password, userDetails.getPassword())) {
            return RespBean.error("用户名或密码不正确");
        } 

使用UserDetail中的isEnabled还可以判断账号是否禁用。

        if (userDetails.isEnabled()){
            return RespBean.error("账号被禁用,请联系管理员");
        } 

若上述的判断都没问题,就可以判断已经登录成功了, 首选注入JwtTokenUtil 

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

生成token  

        //生成token
        String token = jwtTokenUtil.generateToken(userDetails); 

然后新建一个tokenMap集合。在集合中放入Token值,Tokenhead。其中TokenHead通过@value注解注入,然后我们使用RespBean把登录成功的信息返回出去。

    @Value("${jwt.tokenHead}")
    private String tokenHead;
        //生成token
        String token = jwtTokenUtil.generateToken(userDetails);
        Map<String, String> tokenmap = new HashMap<>();
        tokenmap.put("token",token);
        tokenmap.put("tokenHead",tokenHead);
        return RespBean.sucess("登录成功",tokenmap); 

还有地方需要注意一下,登录成功之后,如果我们还需要拿到登录用户的信息,可以使用spring security中的某些对象,例如UserDetails。既然要获取这个对象,当我们登录成功之后,我们要把我们登录的这个对象放在springsecurity的全文中,那下次拿到的这个对象就是我们已经登录过的对象。如果没有放,则可能会出现问题。

我们使用 UsernamePasswordAuthenticationToken这个类,创建对象,第一个参数是userDetails。第二个参数是所谓的凭证,也可以看做是密码。一般置为空,第三个参数就是权限的列表。这样我们就拿到了一个token。有了token之后,我们可以把这个token放在springsecurity的全局里面,具体要怎么放呢,可以使用其getContext()方法,然后调用setAuthentication()方法,参数就是我们获取的token值,这样就可以更新对象了。

/**
         * 在登录成功之后,后期再去获取响应的用户信息的时候,可以使用
         * spring安全框架提供的一些对象。我们需要把登录的用户对象放在spring
         * 安全框架的全文中。下次使用该对象就是我们当前登录用户的对象。
         * 否者会发生问题
         */
        //更新security登录用户的对象
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities());
     //将获取的authenticationToken放置在全局中
     SecurityContextHolder.getContext().setAuthentication(authenticationToken);

在上面我们引入了PasswordEncoder对象,目前还是爆红状态,现在先不管,后面运行的时候再设置。

  

  

 

posted on 2022-05-22 22:34  小蚂蚁啃地球vk  阅读(553)  评论(0)    收藏  举报