完善登录和注销

让用户名匹配才给token

package ml.yompc.myshop.plus.business.service;

import com.google.common.collect.Lists;
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.List;

/**
 * @email yom535@outlook.com
 * @author: 有民(yom535)
 * @date: 2019/9/27
 * @time: 23:48
 */
@Component
public class UserDetailsServiceImpl implements UserDetailsService {
    private static final String USERNAME="admin";
    private static final String PASSWORD="$2a$10$j4NqrVA4GB2QZKOL65POUOaV2aZYValWal6.LIRs50EHcjX8iCI4u";
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        if (username.equals(USERNAME))
        {
            //用户名匹配
            List<GrantedAuthority> grantedAuthorities= Lists.newArrayList();
            GrantedAuthority grantedAuthority=new SimpleGrantedAuthority("USER");
            grantedAuthorities.add(grantedAuthority);
            return new User(USERNAME,PASSWORD,grantedAuthorities);
        }
        else
        {
            //用户名不匹配
            return null;
        }


    }
}

LoginController判断

package ml.yompc.myshop.plus.business.controller;

import com.google.common.collect.Maps;
import ml.yompc.myshop.plus.business.dto.LoginInfo;
import ml.yompc.myshop.plus.business.dto.LoginParam;
import ml.yompc.myshop.plus.commons.dto.ResponseResult;
import ml.yompc.myshop.plus.commons.utils.MapperUtils;
import ml.yompc.myshop.plus.commons.utils.OkHttpClientUtil;
import okhttp3.Response;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.annotation.Resources;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Map;
import java.util.Objects;

/**登录管理Login
 * @email yom535@outlook.com
 * @author: 有民(yom535)
 * @date: 2019/10/17
 * @time: 11:27
 */
@CrossOrigin(origins = "*",maxAge = 3600)
@RestController
public class LoginController {

    private static final String URL_OAUTH_TOKEN="http://localhost:9001/oauth/token";

    @Value("${business.oauth2.grant_type}")
    public  String grantType;

    @Value("${business.oauth2.client_id}")
    public  String clientId;

    @Value("${business.oauth2.client_secret}")
    public  String clientSecret;

    @Resource(name = "userDetailsServiceBean")
    UserDetailsService userDetailsService;

    @Resource
    BCryptPasswordEncoder passwordEncoder;

    @Resource
    TokenStore tokenStore;

    @PostMapping(value = "/user/login")
    public ResponseResult<Map<String,Object>> login(@RequestBody LoginParam loginParam){
        Map<String,String> params=Maps.newHashMap();
        params.put("username", loginParam.getUsername());
        params.put("password", loginParam.getPassword());
        params.put("grant_type", grantType);
        params.put("client_id", clientId);
        params.put("client_secret", clientSecret);

        UserDetails userDetails=userDetailsService.loadUserByUsername(loginParam.getUsername());

        if (userDetails==null||!passwordEncoder.matches(loginParam.getPassword(),userDetails.getPassword()))
        {
            return new ResponseResult<Map<String, Object>>(ResponseResult.CodeStatus.FAIL,"用户名或密码错误",null);
        }
        try {
            Response response = OkHttpClientUtil.getInstance().postData(URL_OAUTH_TOKEN, params);
            String jsonString= Objects.requireNonNull(response.body()).string();
            Map<String, Object> jsonMap = MapperUtils.json2map(jsonString);
            String token = String.valueOf(jsonMap.get("access_token"));
            Map<String,Object> result=Maps.newHashMap();
            result.put("token",token);
            return new ResponseResult<Map<String, Object>>(ResponseResult.CodeStatus.OK,"登录成功",result);

        } catch (Exception e) {
            e.printStackTrace();
        }
        return new ResponseResult<>(ResponseResult.CodeStatus.FAIL,"登录失败",null);
    }

    @GetMapping(value = "/user/info")
    public ResponseResult<LoginInfo> info(){
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        LoginInfo loginInfo =new LoginInfo();
        loginInfo.setName(authentication.getName());


        return new ResponseResult<LoginInfo>(ResponseResult.CodeStatus.OK,"获取用户信息",loginInfo);
    }

    @PostMapping(value = "/user/logout")
    public ResponseResult<Void> logout(HttpServletRequest request){
        String access_token = request.getParameter("access_token");
        OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(access_token);
        tokenStore.removeAccessToken(oAuth2AccessToken);
        return new ResponseResult<Void>(ResponseResult.CodeStatus.OK,"用户注销",null);
    }
}

将状态码做成常量

  public class CodeStatus {
        /**
         * 请求成功
         */
        public static final int OK = 20000;

        /**
         * 请求失败
         */
        public static final int FAIL = 20002;

        /**
         * 非法请求
         */
        public static final int ILLEGAL_REQUEST = 50000;

        /**
         * 非法令牌
         */
        public static final int ILLEGAL_TOKEN = 50008;

        /**
         * 其他客户登录
         */
        public static final int OTHER_CLIENTS_LOGGED_IN = 50012;

        /**
         * 令牌已过期
         */
        public static final int TOKEN_EXPIRED = 50014;
    }

前端UI需要/user/info

@GetMapping(value = "/user/info")
    public ResponseResult<LoginInfo> info(){
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        LoginInfo loginInfo =new LoginInfo();
        loginInfo.setName(authentication.getName());


        return new ResponseResult<LoginInfo>(ResponseResult.CodeStatus.OK,"获取用户信息",loginInfo);
    }

DTO

package ml.yompc.myshop.plus.business.dto;

import lombok.Data;

import java.io.Serializable;

/**登录信息
 * @email yom535@outlook.com
 * @author: 有民(yom535)
 * @date: 2019/10/19
 * @time: 14:04
 */
@Data
public class LoginInfo implements Serializable {
    private String name;
    private String avatar;
}

开启资源服务器并授权

package ml.yompc.myshop.plus.business.configure;

import ml.yompc.myshop.plus.business.service.UserDetailsServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;

/**
 * @email yom535@outlook.com
 * @author: 有民(yom535)
 * @date: 2019/9/27
 * @time: 23:46
 */

@EnableWebSecurity
@EnableResourceServer
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    @Override
    public UserDetailsService userDetailsServiceBean() throws Exception {
        return new UserDetailsServiceImpl();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsServiceBean());
    }

    /**
     * 用于支持 password 模式
     *
     * @return
     * @throws Exception
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring()
                .antMatchers("/user/login");
    }


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        /**
         * 将授权访问配置改为注解方式
         * @see LoginController#info()
         */
        http.exceptionHandling()
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        http.exceptionHandling()
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                // 授权访问
                .antMatchers("/user/info").hasAuthority("USER")
                .antMatchers("/user/logout").hasAuthority("USER");
    }
}

由于返回的参数不对要在frontend/src/api/user.js中更改

import request from '@/utils/request'

export function login(data) {
  return request({
    url: '/user/login',
    method: 'post',
    data
  })
}

export function getInfo(token) {
  return request({
    url: '/user/info',
    method: 'get',
    params: { access_token:token }
  })
}

export function logout(token) {
  return request({
    url: '/user/logout',
    method: 'post',
    params: { access_token:token }
  })
}

完善注销

@PostMapping(value = "/user/logout")
    public ResponseResult<Void> logout(HttpServletRequest request){
        String access_token = request.getParameter("access_token");
        OAuth2AccessToken oAuth2AccessToken = tokenStore.readAccessToken(access_token);
        tokenStore.removeAccessToken(oAuth2AccessToken);
        return new ResponseResult<Void>(ResponseResult.CodeStatus.OK,"用户注销",null);
    }

要返回token参数在frontend/src/api/user.js中更改

export function logout(token) {
  return request({
    url: '/user/logout',
    method: 'post',
    params: { access_token:token }
  })
}