完善登录和注销
让用户名匹配才给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 }
})
}